Lazy load images with knockoutjs - javascript

I am trying to lazy load images with knockoutjs binding.
I was able to lazy load images without knockoutjs framework but I am not sure how can i do that with knockoutjs binding.
Here is my HTML
<div class='liveExample'>
<div class="row">
<div data-bind="foreach: items">
<div class="col-lg-4 col-md-4 col-sm-4 " style="padding: 0px">
<img data-bind=" attr: { src: $data }" class="lazy" />
</div>
</div>
</div>
</div>
Javascript:
var SimpleListModel = function(items) {
this.items = ko.observableArray(items);
bind(this); // Ensure that "this" is always this view model
};
var pictures = []; //Initialise an empty array
for (var i = 0; i = 10 ; i++) {
var image; //This is a placeholder
image = 'http://dummyimage.com/300x200/000/fff&text=' + i; //Set the src attribute (imageFiles[i] is the current filename in the loop)
pictures.push(image); //Append the new image into the pictures array
}
ko.applyBindings(new SimpleListModel(pictures));
Here is the jsfiddle

I have made it work by adding extra div tag before foreach binding and assigning afterrender event to the parent div
<div class="row">
<div data-bind='template: {afterRender: myPostProcessingLogic }'>
<div data-bind="foreach: {data: items} ">
<div class="col-lg-4 col-md-4 col-sm-4 " style="padding: 0px">
<img data-bind=" attr: { 'data-original': $data }" class="lazy" style="min-width:100px; min-height:50px;" />
</div>
</div>
</div>
</div>
Javascript
var SimpleListModel = function(items) {
this.items = ko.observableArray(items);
this.myPostProcessingLogic = function(elements) {
$("img.lazy").lazyload({
effect: "fadeIn",
effectspeed: 2000
});
}
};
var pictures = []; //Initialise an empty array
for (var i = 1; i < 100 ; i++) {
var imageUrl = 'http://dummyimage.com/300x200/000/fff&text=' + i; //Set the image url
pictures.push(imageUrl); //Append the new image into the pictures array
}
ko.applyBindings(new SimpleListModel(pictures));
Here is the jsfiddle

Related

jquery loop add elements onclick

I am really new with javascript
I am trying to loop my json then add the images to my html, then i need to make it so when i click on that image it shows me the File from my json
var obj = JSON.parse(data);
for (index = 0; index < obj.length; ++index) {
var file = obj[index]['File'];
var image = obj[index]['Image'];
var test = $( ".row" ).append(
`<div class="column">
<div class="card">
<img src="${image}" alt="Avatar" style="width:100%">
<div class="container">
<h4>
<b>Title</b>
</h4>
</div>
</div>
</div>`
);
test.click(function(e) {
alert(file);
});
console.log(file);
console.log(image);
}

Appending ng-repeat into HTML element dynamically

I trying to append ng-repeat into my HTML div on load.
It recognizes the ng-repeat, however the index value isn't displaying as it should
HTML
<div id="myInnerCarousel"></div>
controller:
var myCarousel = document.getElementById('myInnerCarousel');
var newItem = document.createElement("div");
newItem.setAttribute("class","item");
var newData = document.createElement("div");
newData.setAttribute("class", "col-xs-6 col-sm-6 col-md-3 col-lg-3");
newData.setAttribute("ng-repeat", "mat in " + arr);
//Create individual values seperately
var newMaterialName = document.createElement("h4");
var nameNode = document.createTextNode("{{mat.Name}}");
newMaterialName.appendChild(nameNode);
//Append everything together
newData.appendChild(newMaterialName);
newItem.appendChild(newData);
myCarousel.appendChild(newItem);
However the result is this:
https://gyazo.com/00b76c6b910d4c6701059d404783f720
It got the right idea of displaying my array, however angular isn't displaying h4 right.
EDIT: imtrying to achieve this in my html:
<div ng-controller="myController">
<div class="item">
<div class="col-xs-6 col-sm-6 col-md-3 col-lg-3" ng-repeat="mat in array1">
<h4>{{mat.Name}}</h4>
</div>
</div>
<div class="item">
<div class="col-xs-6 col-sm-6 col-md-3 col-lg-3" ng-repeat="mat in array2">
<h4>{{mat.Name}}</h4>
</div>
</div>
Lets simply state that this is not the way to do it.
A better and more angular way would be (assuming your apps name is app).
HTML
<div ng-controller="myController">
<div class="item">
<div class="col-xs-6 col-sm-6 col-md-3 col-lg-3" ng-repeat="mat in array">
<h4>{{mat.Name}}</h4>
</div>
</div>
</div>
Angular Controller
angular.module('app').controller('myController', function($scope) {
$scope.array = [{Name: 'abc'}, {Name: 'def'}]; // or whatever your data looks like.
});
Dynamically added component should be compiled using angular. you can use $compile function of angular. Below is working code.
Jsfiddle
function TodoCtrl($scope, $compile) {
$scope.arr = [{Name: "Dynamically Added cowermponent"}];
var myCarousel = document.getElementById('myInnerCarousel');
var newItem = document.createElement("div");
newItem.setAttribute("class","item");
var newData = document.createElement("div");
newData.setAttribute("class", "col-xs-6 col-sm-6 col-md-3 col-lg-3");
newData.setAttribute("ng-repeat", "mat in arr");
//Create individual values seperately
var newMaterialName = document.createElement("h4");
var nameNode = document.createTextNode("{{mat.Name}}");
newMaterialName.appendChild(nameNode);
//Append everything together
newData.appendChild(newMaterialName);
newItem.appendChild(newData);
myCarousel.appendChild(newItem);
$compile(newItem)($scope);
}

How can I get specific information from an external api to my ng-repeat

main.html
<div class="row" ng-repeat="post in myBlogPosts.slice().reverse()">
<br>
<div class="col-md-9 text-center">
<a href="#/blog-post/{{post._id}}">
<div class="thumbnail mTextBg customShadow">
<br>
<img class="img-responsive" src="http://placekitten.com/700/400" alt="">
<div class="caption">
<h3>{{post.imdbId}}</h3>
<p>{{post.blogContent}}</p>
</div>
</div>
</a>
</div>
<div class="col-md-3">
// I WANT THIS PART !!
<div class="well sideBars customShadow">
<img class="img-responsive" ng-src="{{film.Poster}}" title="{{film.Title}}">
<h4 class="text-center">{{film.Title}}</h4>
<p class="text-center" style="margin-bottom: 2px;"><b>Year:</b> {{film.Year}}</p>
<p class="text-center"><span class="customMargin">Runtime: {{film.Runtime}}</span></p>
<p class="text-center"><span class="customMargin">Director: {{film.Director}}</span></p>
<p class="text-center"><span class="customMargin">Writer: {{film.Writer}}</span></p>
<p class="text-center"><span class="customMargin">Actors: {{film.Actors}}</span></p>
</div>
</div>
</div>
This is part of my main.html . In h3 and p tags, I get imdbId and blogContent from my database and put it in ng-repeat in order to traverse blog posts in list. I want to be able get other information(under // I WANT THIS PART) for every post in myBlogPost.
MainController.js
var refresh = function() {
$http.get('/myDatabase').success(function(response) {
$scope.myBlogPosts = response;
});
};
refresh();
This part work as expected when page loaded.
I need also these parts in Main Controller ;
var onGetFilmData = function (data) {
$scope.film = data;
};
var onError = function (reason) {
$scope.error = reason;
};
imdb.getImdbInfo(-- need Id --).then(onGetFilmData, onError);
But I need to put each post id somehow in order to get specific data from Imdb api.
Imdb.js
(function(){
var imdb = function($http){
var getImdbInfo = function (id) {
return $http.get('http://www.omdbapi.com/?i=' + id + '&plot=short&r=json')
.then(function(response){
return response.data;
});
};
return{
getImdbInfo: getImdbInfo
};
};
var module = angular.module('myApp');
module.factory('imdb', imdb);
})();
If I delete id part and put a specific id string in getImdbInfo function, all post in main.html fill with just one film information. I want to fetch those data for each film in my database(I am holding imdb id of each film in my database).
MainController
var jsonObj = {};
var refresh = function() {
$http.get('/myDatabase').success(function(response) {
jsonObj = response;
for(var i = 0; i < jsonObj.length ; i++){
jsonObj[i].title = '';
}
for(var i = 0; i < jsonObj.length ; i++){
(function(i) {
imdb.getImdbInfo(jsonObj[i].imdbId).then(function (data) {
jsonObj[i].title = data.Title;
});
})(i);
}
$scope.myBlogPosts = jsonObj;
});
};
refresh();
main.html
<div class="row" ng-repeat="post in myBlogPosts.slice().reverse()">
<br>
<div class="col-md-9 text-center">
<a href="#/blog-post/{{post._id}}">
<div class="thumbnail mTextBg customShadow">
<br>
<img class="img-responsive" src="http://placekitten.com/700/400" alt="">
<div class="caption">
<h3>{{post.imdbId}}</h3>
<p>{{post.blogContent}}</p>
</div>
</div>
</a>
</div>
<div class="col-md-3">
<!-- Side Widget Well -->
<div class="well sideBars customShadow">
<h4 class="text-center">{{post.title}}</h4>
</div>
</div>
</div>
I solve my problem with adding response from Imdb to my json object which is coming from database. So I can easily use them in ng-repeat.

how to create generic html with javascript

I have the following html:
<div id="prog" class="downloads clearfix">
<div class="item">
<div class="image_container">
<img src="/img/downloads/company.png" width="168" height="238" alt="">
</div>
<div class="title">
pricelist: <label id="pr1"></label>
</div>
<div class="type">
pdf document
</div>
<div class="link">
<a id="pdfdocument" class="button" target="_blank" href="#">start Download </a>
</div>
</div>
</div>
I want build HTML which is inside the <div id="prog"> with Javascript:
<div id="prog" class="downloads clearfix"></div>
I'm trying to use this Javascript, but without success:
var tmpDocument, tmpAnchorTagPdf, tmpAnchorTagXls, parentContainer, i;
parentContainer = document.getElementById('prog');
for (i = 0; i < documents.length; i++) {
tmpDocument = documents[i];
tmpAnchorTagPdf = document.createElement('a id="pdfdocument" ');
tmpAnchorTagPdf.href = '/role?element=' + contentElement.id + '&handle=' + ope.handle;
tmpAnchorTagPdf.innerHTML = 'start Download';
tmpAnchorTagXls = document.createElement('a');
tmpAnchorTagXls.href = '/role?element=' + contentElement.id + '&handle=' + ope.handle;
tmpAnchorTagXls.innerHTML = 'start Download';
parentContainer.appendChild(tmpAnchorTagPdf);
parentContainer.appendChild(tmpAnchorTagXls);
}
If this is a section of code that you will be using more than once, you could take the following approach.
Here is the original div without the code you want to create:
<div id="prog" class="downloads clearfix">
</div>
Create a template in a hidden div like:
<div id="itemtemplate" style="display: none;">
<div class="item">
<div class="image_container">
<img src="/img/downloads/company.png" width="168" height="238" alt="">
</div>
<div class="title">
pricelist: <label></label>
</div>
<div class="type">
pdf document
</div>
<div class="link">
<a class="button" target="_blank" href="#">start Download </a>
</div>
</div>
</div>
Then duplicate it with jquery (OP originally had a jquery tag; see below for JS), update some HTML in the duplicated div, then add it to the document
function addItem() {
var item = $("#itemtemplate div.item").clone();
//then you can search inside the item
//let's set the id of the "a" back to what it was in your example
item.find("div.link a").attr("id", "pdfdocument");
//...the id of the label
item.find("div.title label").attr("id", "pr1");
//then add the objects to the #prog div
$("#prog").append(item);
}
update
Here is the same addItem() function for this example using pure Javascript:
function JSaddItem() {
//get the template
var template = document.getElementById("itemtemplate");
//get the starting item
var tempitem = template.firstChild;
while(tempitem != null && tempitem.nodeName != "DIV") {
tempitem = tempitem.nextSibling;
}
if (tempitem == null) return;
//clone the item
var item = tempitem.cloneNode(true);
//update the id of the link
var a = item.querySelector(".link > a");
a.id = "pdfdocument";
//update the id of the label
var l = item.querySelector(".title > label");
l.id = "pr1";
//get the prog div
var prog = document.getElementById("prog");
//append the new div
prog.appendChild(item);
}
I put together a JSFiddle with both approaches here.

AngularJS not filtering products list

I am having an issue with AngularJS filtering my products list. I have done a few console.log queries on my variables and all seem fine. The problem is that the view does not update to show the filtered products.
Filtering works perfectly fine when you enter the search text in the input box but it does not work when clicking on the Category menu items.
I would like to filter the product list by category when the user clicks on the menu item.
Please see my code below and any help or advice is greatly appreciated.
My app.js
myApp.controller('StoreController', function($scope, $filter, storeFactory, cartFactory) {
$scope.cartTotal = 0;
$scope.cartItems = [];
$scope.categories = [];
$scope.counted = 0;
$scope.filteredProducts = {};
//get the products
storeFactory.getProducts(function(results) {
$scope.products = results.products;
$scope.counted = $scope.products.length;
$scope.filteredProducts = results.products;
});
$scope.$watch("search", function(query){
if($scope.filteredProducts.length) {
$scope.filteredProducts = $filter("filter")($scope.products, query);
$scope.counted = $scope.filteredProducts.length;
}
});
$scope.filterProductsByCategory = function(categoryName){
console.log('category filter');
/*$scope.products.forEach(function(o,i){
console.log('filter');
if( o.category_id !== categoryId ){
$scope.filteredProducts.splice(i,1);
console.log('removing');
console.log(o);
}
});*/
$scope.filteredProducts = $filter("filter")($scope.filteredProducts, categoryName);
console.info('the filtered items');
console.log($scope.filteredProducts);
$scope.counted = $scope.filteredProducts.length;
}
$scope.getCategories = function(){
storeFactory.getCategories(function(results){
$scope.categories = results.rows;
});
}
$scope.getCategories();
});
My store.htm
UPDATE :
I removed the extra controller reference and wrapped everything in one div.
<div ng-controller="StoreController">
<!-- the sidebar product menu -->
<div class="block-content collapse in">
<div class="daily" ng-repeat="category in categories | orderBy:'name'">
<div class="accordion-group">
<div ng-click="filterProductsByCategory(category.category_name)" class="accordion-toggle collapsed">{{category.category_name}}</div>
</div>
</div>
</div>
</div>
<!-- Load store items start -->
<div class="label">Showing {{counted}} Product(s)</div>
<div class="row" ng-repeat="product in filteredProducts | orderBy:'product_name'" style="margin-left: 0px; width: 550px;">
<hr></hr>
<div class="span1" style="width: 120px;">
<!-- <a data-toggle="lightbox" href="#carouselLightBox"> -->
<a data-toggle="lightbox" href="#carouselLightBox{{product.product_id}}">
<!--img alt={{ product.product_name }} ng-src="{{product.image}}" src="{{product.image}}" /-->
<img id="tmp" class="" src="images/products/{{product.product_image_filename}}" alt=""></img>
</a>
<div class="lightbox hide fade" id="carouselLightBox{{product.product_id}}" style="display: none;">
<div class='lightbox-content'>
<img src="images/products/{{product.product_image_filename}}" alt="" />
<!--img alt={{ product.product_name }} ng-src="{{product.image}}" src="{{product.image}}" /-->
<button class="btn btn-primary" id="close_lightbox" ng-click="closeBox(product.product_id, $event)">Close</button>
</div>
<style>
#close_lightbox
{
position: absolute;
top: 5px;
right: 5px;
}
</style>
</div>
</div>
<div class="span6" style="width: 330px; margin-bottom: 15px;">
<h5 style="font-size: 14px; font-weight: bold;">{{product.product_name}}</h5>
<p>{{product.product_description }}</p>
<p>Category : {{product.category_name}}</p>
</div>
<div class="span3">
<p class="price">Price : <strong>R{{ product.product_price }}</strong></p>
</div>
</div>
<!-- end of controller -->
</div>
$scope.filteredProducts = {};
//get the products
storeFactory.getProducts(function (results) {
$scope.products = results.products;
$scope.counted = $scope.products.length;
$scope.filteredProducts = results.products;
});
Is results.products an array of objects or an object of objects? Because $filter('filter')($scope.products,query); expects $scope.products to be an array.
$scope.$watch("search", function (query) {
if($scope.filteredProducts.length) {
$scope.filteredProducts = $filter("filter")($scope.products, query);
$scope.counted = $scope.filteredProducts.length;
}
});
Here's what I'm thinking this should look like and you won't need the $watch statement or the filteredProducts array/object, In controller:
$scope.search = '';
$scope.products = [];
$scope.categories = ['category1','category2','category3'];
// assuming that your store function getProducts returns a promise here
storeFactory.getProducts().then(function(results){
$scope.products = results.products; // needs to be an array of product objects
});
$scope.filterProductsByCategory = function(category){
$scope.search = category;
};
Then in your HTML Partial, this is not exactly how your's is, I'm just showing you here in shorter form what is possible:
<button ng-repeat="cat in categories" ng-click="filterProductsByCategory(cat)">{{cat}}</button>
<div class="row" ng-repeat="product in products | filter:search | orderBy:'product_name'">
<!-- Product Information Here -->
</div>
I created a JSFiddle to demonstrate: http://jsfiddle.net/mikeeconroy/QL28C/1/
You can free form search with any term or click a button for a category.
The side bar is using one instance of StoreController, and the main div is using another instance of StoreController. Each instance having its own scope. The should both use the same controller instance: wrap everything inside a div, and use a unique StoreController for this wrapping div.

Categories