Sorting with isotope from external json rendered with handlebars - json

Can't seem to get Isotope sort to work :(
On the client side of a webpage I'm displaying data which comes from an external json with a template using handlebars.js.
I want the users to be able to sort, filter and search the data that is displayed. I've seen that with Isotope can this be achieve successfully. I did manage to get filtering to work.
However I'm stuck with sorting in targeting the class of the object with the getSortData option which value comes from the json.
Example of the JSON structure with the price:
Here is the code trying to sort by price, first my menu:
<ul id="sort">
<li>original order</li>
<li>number</li>
</ul>
Then my handlebars template, where I want to reach the p.class = number:
<div id="mcContainer"></div>
<script id="mcTemplate" type="text/x-handlebars-template">
{{#each this}} {{#annoncer}}
<article class="mc_item {{category}} {{year}}">
<a data-single href="{{id}}">
<h3>{{brand}} {{model}}</h3>
<img src={{images.0.small}} />
<h4 class="mc_aar">ÅR: {{year}}, {{km}} km</h4>
<p>{{category}}</p>
<p class="mc_pris number">{{price}},-</p>
<hr>
</a>
</article>
{{/annoncer}} {{/each}}
</script>
And my javascript file:
(function ($) {
"use strict";
// javascript code here. i.e.: $(document).ready( function(){} );
$(document).ready(function ($) {
var $container = $('#mcContainer');
$.ajax({
url: "http://diegovega.dk/kea/2semester/json-eks/json-eks.json",
method: "GET",
dataType: 'json',
success: function (response) {
var template = $('#mcTemplate').html();
var renderer = Handlebars.compile(template);
var result = response;
$('#mcContainer').html(renderer(result));
runIsotope();
}
});
function runIsotope() {
var $items = $('.mc_item');
$items.isotope({})
$items.isotope('reloadItems')
.isotope({
itemSelector: '.mc_item',
layoutMode: 'fitRows',
fitRows: {
gutter: 20
},
getSortData: {
number: '.number parseInt'
},
});
// Sort based on price
$('#sort').on('click', function () {
if ($(this).hasClass('checked')) {
$(this).removeClass('checked');
.isotope({
sortBy: 'original-order'
});
} else {
$('#sort').removeClass('checked');
var sortValue = $(this).attr('data-sort-value');
console.log($(this).attr('data-sort-value'));
.isotope({
sortBy: sortValue
});
$(this).addClass('checked');
}
});
} //RUN ISOTOPE
}); // END DOCUMENT READY
})(jQuery); // END use strict
Any help is greatly appreciated :)

Initialize Isotope on the container, not the items
Use data-sort attribute on the links click

Related

It's possible to 'GET' multiple XML files with javascript?

I want to add data that's stored in XML Files to the HTML View with handlebars.js but,
Instead of make a GET of 1 url ex:http://json.org/example.html i will want to add multiple XML Files. I will aprreciate any help on this
Thanks in advance!
var ourRequest = new XMLHttpRequest();
ourRequest.open('GET', 'https://learnwebcode.github.io/json-example/pets-data.json');
ourRequest.onload = function() {
if (ourRequest.status >= 200 && ourRequest.status < 400) {
var data = JSON.parse(ourRequest.responseText);
createHTML(data);
} else {
console.log("We connected to the server, but it returned an error.");
}
};
ourRequest.onerror = function() {
console.log("Connection error");
};
ourRequest.send();
Handlebars.registerHelper("calculateAge", function(birthYear) {
var age = new Date().getFullYear() - birthYear;
if (age > 0) {
return age + " years old";
} else {
return "Less than a year old";
}
});
function createHTML(petsData) {
var rawTemplate = document.getElementById("petsTemplate").innerHTML;
var compiledTemplate = Handlebars.compile(rawTemplate);
var ourGeneratedHTML = compiledTemplate(petsData);
var petsContainer = document.getElementById("pets-container");
petsContainer.innerHTML = ourGeneratedHTML;
}
<div class="page-wrap">
<h1>Handlebars js</h1>
<div id="pets-container"></div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.5/handlebars.min.js"></script>
<script id="petsTemplate" type="text/x-handlebars-template">
{{#each pets}}
<div class="pet">
<div class="photo-column">
<img src="{{photo}}">
</div>
<div class="info-column">
<h2>{{name}} <span class="species">({{species}})</span></h2>
<p>Age: {{calculateAge birthYear}}</p>
{{#if favFoods}}
<h4 class="headline-bar">Favorite Foods</h4>
<ul class="favorite-foods">
{{#each favFoods}}
<li>{{{this}}}</li>
{{/each}}
</ul>
{{/if}}
</div>
</div>
{{/each}}
</script>
What you need is a single callback that gets executed only when all the data you need from your various requests has been fetched. To achieve this you will need some sort of synchronization between the various AJAX calls you're doing.
Promise pattern, the Q library, which is one of the several implementations of the pattern. They have done most of the hard work of synchronizing multiple AJAX requests for us.
I will post an example here:
function xmlPromise(name) {
return Q.promise(function (resolve, reject, notify) {
$.ajax({
type: "GET",
dataType: "xml",
async: true,
url: name,
contentType: "text/xml; charset=UTF-8"
})
.done(function (data) {
resolve(data);
}).fail(function () {
reject();
});
});
};
//your xml files can be stored in the promises variable
var promises = [ xmlPromise('your-xml-file-1.xml'), xmlPromise('your-xml-file-2.xml') ];
var results = [];
Q.allSettled(promises).then(function(responses) {
results.push(responses[0].value);
results.push(responses[1].value);
});
Hope it help

Angular service not storing data between two controllers

I am trying to use a service to set title in controller1 and then access title in controller2.
sharedProperties.setTitle(title) works in controller1, but when I try to get the title in controller2, it gets "title" (the initial value) instead of the new value.
I've also tried storing title in an object but it didn't work.
app.service('sharedProperties', function () {
var title = "title"
return {
getTitle: function () {
return title;
},
setTitle: function (val) {
title = val;
}
}
});
app.controller('controller1', ['$scope', 'sharedProperties', function ($scope, sharedProperties) {
$('body').on("click", "button[name=btnListItem]", function () {
// gets the title
var title = $(this).text();
// sets the title for storage in a service
sharedProperties.setTitle(title);
});
}]);
app.controller('controller2', ['$scope', 'sharedProperties', function ($scope, sharedProperties) {
$scope.sharedTitle = function() {
return sharedProperties.getTitle();
};
}]);
And in my view, I have {{ sharedTitle() }} which should, as I understand it, update the title text with the new title.
Also, in case this is relevant: the two controllers are linked to two different html pages.
What am I doing wrong?
EDIT
Updated button listener:
$('body').on("click", "button[name=btnListItem]", function () {
// gets the text of the button (title)
var title = $(this).text();
sharedTitle(title);
alert(sharedProperties.getTitle());
document.location.href = '/nextscreen.html';
});
$scope.sharedTitle = function (title) {
sharedProperties.setTitle(title);
};
It seems to be correct in your sample code. I setup jsfiddle and it seems work correctly. Finding out a difference between my jsfiddle and your actual code would help you to find the problem you should solve.
Javascript:
angular.module('testapp', [])
.service('sharedProperties', function(){
var title = 'title';
return {
getTitle: function(){
return title;
},
setTitle: function(val){
title = val;
}
};
})
.controller('controller1', function($scope, sharedProperties){
$scope.change_title = function(newvalue){
sharedProperties.setTitle(newvalue);
};
})
.controller('controller2', function($scope, sharedProperties){
$scope.sharedTitle = function(){
return sharedProperties.getTitle();
};
})
Html:
<div ng-app="testapp">
<div ng-controller="controller1">
<input ng-model="newvalue">
<button ng-click="change_title(newvalue)">Change Title</button>
</div>
<div ng-controller="controller2">
<span>{{sharedTitle()}}</span>
</div>
</div>
My jsfiddle is here.
You have to print console.log(sharedProperties.getTitle()); Dont need return from controller.
So your code of controller2 is $scope.sharedTitle = sharedProperties.getTitle();
You need to use the $apply so that angular can process changes made outside of the angular context (in this case changes made by jQuery).
$('body').on("click", "button[name=btnListItem]", function () {
// gets the title
var title = $(this).text();
// sets the title for storage in a service
$scope.$apply(function() {
sharedProperties.setTitle(title);
});
});
See plunker
That said, this is BAD PRACTICE because you're going against what angular is meant for. Check “Thinking in AngularJS” if I have a jQuery background?. There are cases when you need to use $apply like when integrating third party plugins but this is not one of those cases.

call a function after completely loaded into html with ng-repeate

how can I call a function after completely loaded my json data from the server into a ng-repeate?
Ex: I have json URL it have some products data which is in my car, once I clicked on view cart the cart items should be showed in the popup box. and if add another product it should me added in the popup box.
Please help me guys
HTML
<div data-ng-controller="MainController">
<ul>
<li ng-repeat="asset in assets" my-directive>{{asset}}</li>
</ul>
</div>
JS
var app = angular.module('myApp', []);
app.controller('MainController',function($scope) {
$scope.assets = [1,2,3,4,5,6,7,8,9,10];
});
app.value('myFunc',function(){
alert('Hello');
});
app.directive('myDirective', function( myFunc) {
return {
restrict: 'A',
link: function(scope, elem) {
if (scope.$last){
myFunc();
}
}
};
});
It should work also with a asyn service (to get data)

jQuery click function and list items

Lately, I'm having trouble with jQuery click events. In this example, I'm wanting to use ajax post when an user clicks on a list item. No errors pop up on Firebug and the jquery script is in the code. When I run the code, nothing happens. The code is below.
<script type="text/javascript" >
$('#pop').click(function() {
var pop = 'pop';
$.post('ajax_file.php', {
style: pop
}, function(data) {
$('#tube').html(data);
});});
</script>
<ul>
<li id="pop">Pop</li>
</ul>
Try this:
$(document).ready(function(){
$('#pop').click(function() {
var pop = 'pop';
$.post('ajax_style_homepage.php', {
style: pop
}, function(data) {
$('#tube').html(data);
});
});
})
Wrap it in the document ready function and see what happens. Also you dont need to empty. You can call the html method and it will overwrite the content.
$(function(){
$('#pop').click(function() {
var pop = 'pop';
$.post('ajax_style_homepage.php', { style: pop}, function(data) {
$('#tube').html(data);
});
});
});
Just another way to do the same thing as the 2 other answers without binding your click method at page load. Simply set your onclick attribute on your list item to a function containing your ajax call. This is easier to follow in my opinion.
<script type="text/javascript" >
function makeAJAXPost(){
var pop = 'pop';
$.post('ajax_file.php', {
style: pop
}, function(data) {
$('#tube').html(data);
});
}
</script>
<ul>
<li onclick="makeAJAXPost()" id="pop">Pop</li>
</ul>

How to use HTML5 drag and drop in combination with KnockoutJS?

I can't seem to bind to html5 drag and drop events.
Here's an example of from a template:
<script id="tabsTemplate" type="text/html">
<div class="dropzone" for="tab"
data-bind="event:{dragover: function(event){event.preventDefault();},
dragenter: function(event){event.target.addClass('dragover'); event.preventDefault();},
dragleave: function(event){event.target.removeClass('dragover'); event.preventDefault();}}
drop: function(event){console.log('blahblah!')}"></div>
<h1 class="tab" draggable="true"
data-bind="attr: {selected: $data.name === $item.selected()},
click: function(){$item.selected($data.name)},
event:{ dragstart: function(event){console.log('blah!!')},
dragend: function(event){document.getElementsByClassName('dragover')[0].removeClass('dragover')}}">
${name}
<img src="icons/close-black.png" class="close button" role="button"
data-bind="click: function(e){$item.close($data)}">
</h1>
</script>
What I have should work as expected... and it does as long as I make them normal inline ones. However, then the other bindings don't work!
I am getting this error message:
Uncaught SyntaxError: Unexpected token '||' jquery-tmpl.js:10
What's going on here? Is there something I'm doing wrong?
OK, I have worked it out. It seems I missed in the documentation where it said that in knockout, by default it makes all events prevent default / return false. So all I had to do was make my dragstart handler return true, and now it works. Phew!!
For those (like me) who need a SSCCE working; the solution follow's [cybermotron] suggestion, also fixes an issue where handlers expect prarameters data and event.
http://jsfiddle.net/marrok/m63aJ/
HTML
<script type="application/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<ul id="people" data-bind='template: { name: "personTmpl", foreach: people }'>
</ul>
<div class="trash" data-bind ="visible:dragging, event:{
dragover: function(data, event){
event.preventDefault();
},
drop: function(data, event){
console.log('trash', $root.drag_start_index())
$root.trash($root.drag_start_index())
event.preventDefault();
}
}"> <span>DELETE</span> </div>
<script id="personTmpl" type="text/html">
<li class="draggable" draggable="true" data-bind="event:{
dragstart: function(data, event){
$(event.target).addClass('dragSource')
$root.drag_start_index($index());
return $(event.target).hasClass('draggable');},
dragend: function(data, event){
$root.drag_start_index(-1);
$(event.target).removeClass('dragSource')
return true;
},
dragover: function(data, event){event.preventDefault();},
dragenter: function(data, event){
$root.drag_target_index($index());
var element = $(event.target)
if(element.hasClass('draggable'))
element.toggleClass('dragover');
event.preventDefault();
},
dragleave: function(data, event, $index){
var element = $(event.target)
if(element.hasClass('draggable'))
element.toggleClass('dragover');
event.preventDefault();
},
drop: function(data, event){
$(event.target).removeClass('dragover');
console.log('swap', $root.drag_start_index(), $root.drag_target_index() )
$root.swap($root.drag_start_index(), $root.drag_target_index())
}
}">
<span data-bind='text: name'></span>
</li>
</script>
Knockout
var Person = function(name) {
this.name = ko.observable(name);
};
var PeopleModel = function() {
var self = this;
self.drag_start_index = ko.observable();
self.drag_target_index = ko.observable();
self.dragging = ko.computed(function() {
return self.drag_start_index() >= 0;
});
self.people = ko.observableArray([
new Person("Oleh"), new Person("Nick C."), new Person("Don"), new Person("Ted"), new Person("Ben"), new Person("Joe"), new Person("Ali"), new Person("Ken"), new Person("Doug"), new Person("Ann"), new Person("Eve"), new Person("Hal")]);
self.trash = function(index) {
self.people.splice(index, 1)
}
self.swap = function(from, to) {
if (to > self.people().length - 1 || to < 0) return;
var fromObj = self.people()[from];
var toObj = self.people()[to];
self.people()[to] = fromObj;
self.people()[from] = toObj;
self.people.valueHasMutated()
}
};
ko.applyBindings(new PeopleModel());​
You might have the same problem as mentioned here, although it refers to nested templates:
Warning
If you are passing templateOptions to the template binding from a nested template (so, specifying a template binding from within a template), then pay special attention to your syntax. You will encounter a problem, if your binding looks like this:
<div data-bind="template: { name: 'items', data: newItems, templateOptions: { header: “New Items!”}}"></div>
The jQuery Templates plugin gets confused by the }} at the end of your binding, since that is part of its syntax. Adding a space between your braces will work fine. Hopefully this prevents someone from a little unnecessary frustration.
<div data-bind="template: { name: 'items', data: newItems, templateOptions: { header: “New Items!”} }"></div>