Bootstrap Responsive Product Grid - html

I am building a product grid built upon AngularJS data - where there will be images and product details (text)
The text sometimes extends to the 2nd line, causing havoc.
Here is my code:
<div class="row">
<div data-ng-repeat="s in Results">
<div class="col-xs-4">
<a href="#" class="thumbnail">
<div>
<img ng-src="{{s.ProductImage}}" class="img-responsive">
</div>
<div>
{{s.Store}} {{s.Category}} {{s.ProductName}}
</div>
</a>
</div>
<div class="clearfix visible-xs-block"></div>
</div>
</div>
This is what it looks like:
How do I fix it so that <div>s all have the same height?
I tried to look online for solutions, but I think I am 50% there. Please help.
Note: I Don't want to hide content.

This is what I ended up doing for anyone who stumbles on this again in the future.
Javascript
function ResizeToLargestElement(element) {
var maxHeight = -1;
if ($(element).length > 0) {
$(element).each(function () {
maxHeight = maxHeight > $(this).height() ? maxHeight : $(this).height();
});
$(element).each(function () {
$(this).height(maxHeight);
});
}
}
Without AngularJS
For those who aren't using AngularJS, just call ResizeToLargestElement() when data changes or the window is resized using
$(window).resize(function() {
ResizeToLargestElement('.AutoResizeToLargestElement');
});`
With AngularJS
The idea is to call the ResizeToLargestElement() function whenever $scope.Results changes or when the window resizes.
To know when $scope.Results changed is easy, but to know when elements (that are bound to it) finished rendering is not easy. To do that, you need a AngularJS directive.
To know when the window re-sizes, use angular.element($window).on('resize', function () {...});
HTML
<div class="row">
<div data-ng-repeat="s in Results" data-ng-repeat-finished> <!--ADDED Directive-->
<div class="col-xs-4">
<a href="#" class="thumbnail AutoResizeToLargestElement"> <!--ADDED Class-->
<div>
<img ng-src="{{s.ProductImage}}" class="img-responsive">
</div>
<div>
{{s.Store}} {{s.Category}} {{s.ProductName}}
</div>
</a>
</div>
<!--REMOVED clearfix-->
</div>
</div>
MyDirectives.js
angular.module('myApp').directive('ngRepeatFinished', function ($timeout) {
return {
restrict: 'A',
link: function (scope, element, attr) {
if (scope.$last === true) {
$timeout(function () {
scope.$emit('ngRepeatFinished');
});
}
}
}
});
mycontroller.js
$scope.$on('ngRepeatFinished', function (ngRepeatFinishedEvent) {
ResizeToLargestElement(".AutoResizeToLargestElement");
});
angular.element($window).on('resize', function () {
ResizeToLargestElement(".AutoResizeToLargestElement");
});
Note: this requires you to include $window in the AngularJS dependency list.
I.E. angular.module('myApp').controller('....', ['...., '$window', ....]);

If you want to keep the height of each product dynamic, you will need to split the results into columns. And then use ng-if to put the right items in the right column. Every 3rd item will go into the same column. To set them to different columns, just reduce the $index by 1 for each extra column.
<div class="row">
<div class="col-xs-4">
<div ng-repeat="s in Results"> <a href="#" class="thumbnail" ng-if="$index%3==0">
<div>
<img ng-src="{{s.ProductImage}}" class="img-responsive" />
</div>
<div>{{s.Store}} {{s.Category}} {{s.ProductName}}</div>
</a>
<div class="clearfix visible-xs-block"></div>
</div>
</div>
<div class="col-xs-4">
<div ng-repeat="s in Results"> <a href="#" class="thumbnail" ng-if="($index-1)%3==0">
<div>
<img ng-src="{{s.ProductImage}}" class="img-responsive" />
</div>
<div>{{s.Store}} {{s.Category}} {{s.ProductName}}</div>
</a>
<div class="clearfix visible-xs-block"></div>
</div>
</div>
<div class="col-xs-4">
<div ng-repeat="s in Results"> <a href="#" class="thumbnail" ng-if="($index-2)%3==0">
<div>
<img ng-src="{{s.ProductImage}}" class="img-responsive" />
</div>
<div>{{s.Store}} {{s.Category}} {{s.ProductName}}</div>
</a>
<div class="clearfix visible-xs-block"></div>
</div>
</div>
</div>

You can manually restrict the height of the div and use an overflow. Use something like
<div class="col-xs-4" style="height:200px; overflow: auto;">Content</div>
Or, you could always use a responsive table (<table class="table table-responsive">) for your layout.

The best solution for this problem is to add clearfix class to hidden divs for earch size of viewport. for example:
Large size: i want 6 columns (large desktops)
Medium size: i want 4 columns (Desktops)
Tablets: i want 3 coumns
phones: i want 2 columns
so i have the products list:
<?php $count=1; foreach($productList as $product) : ?>
<div class="item col-xs-6 col-sm-4 col-md-3 col-lg-2">
Picture and description
</div><!-- /item -->
<?php if( ($count%2)==0) {?><div class="clearfix visible-xs-block"></div><?php }?>
<?php if( ($count%3)==0) {?><div class="clearfix visible-sm-block"></div><?php }?>
<?php if( ($count%4)==0) {?><div class="clearfix visible-md-block"></div><?php }?>
<?php if( ($count%6)==0) {?><div class="clearfix visible-lg-block"></div><?php }?>
<?php $count++; endforeach?>

Related

Can't access classes inside a react-modal

This is my first time using react and I am sure that code given is probably not using react's best practices but I am just playing around with it to understand it for now, my problem is that I am trying to access the divs and paragraphs inside the modal but document querySelector returns null. Any ideas? Thanks
moreInfo(movie) {
const movieModalImg = document.querySelector(".movieModalImg");
const movieModalTitle = document.querySelector(".movieModalTitle");
const movieModalRunTimeNum = document.querySelector(
".movieModalRunTimeNum"
);
}
render() {
return (
<div>
<div id="modal">
<Modal
isOpen={this.state.setIsOpen}
onRequestClose={this.closeModal.bind(this)}
contentLabel="Example Modal"
>
<a className="closeModalBtn" onClick={this.closeModal} href={"#"}>
×
</a>
<div className="modal-wrapper">
<div>
<img src={moviePoster} className="movieModalImg" alt=""></img>
</div>
<p className="movieModalTitle"></p>
<div className="movieModalRunTime">
<h4>Runtime:</h4>
<p className="movieModalRunTimeNum"></p>
<p>Minutes</p>
</div>
<div className="movieModalProductionCountry">
<h4>Production Countries:</h4>
<p className="movieModalProductionCountries"></p>
</div>
<div className="movieModalVoteCount">
<h4>Vote Count:</h4>
<p className="movieModalVoteCountNum"></p>
</div>
<div className="movieModalRevenew">
<h4>Revenew:</h4>
<p className="movieModalRevenewNum"></p>
<p>$</p>
</div>
<div className="movieModalBudget">
<h4>Budget:</h4>
<p className="movieModalBudgetNum"></p>
<p>$</p>
</div>
</div>
</Modal>
</div>
<a
href="#"
className="movieCoverMoreInfo"
onClick={this.moreInfo.bind(this)}
>
<img className="moreInfoIcon" src={info} alt=""></img>
More Info
</a>
</div>
);
}

Unable to List the materialize css cards in the same row

I am making a webpage where i fetch movies using an api and i am trying to list the movie posters using the materilize css card in the same row, but it only gets listed on top of each other as a column.
app.js
class App extends React.Component {
constructor(props) {
super(props);
this.state={};
this.performSearch()
}
performSearch(){
$.ajax({
url:urlString,
success:(searchResults) =>{
console.log("Feteched succesfully");
//console.log(searchResults);
const results = searchResults.results
//console.log(results[0])
const movieRows = [];
results.forEach((movie) =>{
// console.log(movie.title);
console.log(movie.poster_path)
const movieRow = <MovieLists movie={movie}/>
movieRows.push(movieRow);
})
this.setState({rows:movieRows})
},
error:(xhr,status,err) =>{
console.error("Failed to fetch");
}
})
}
render() {
return (
<div className="App">
{this.state.rows}
</div>
);
}
}
export default App;
MovieLists.js
<div className="container">
<div className="row">
<div className="col s12">
<Movie key ={props.movie.id} image={props.movie.poster_path}
title={props.movie.title}/>
</div>
</div>
</div>
Movie.js
<div className="col s12 m6 l3">
<div className="card" >
<div className="card-image waves-effect waves-block waves-dark">
{
props.image == null ?
<img src ={'https://www.google.com/url?sa=i&url=https%3A%2F%2Fmarket.ionicframework.com%2Fstarters%2Fomdb-search&psig=AOvVaw3Beb0rUUVxt3OsEfwzu07Q&ust=1585542435018000&source=images&cd=vfe&ved=0CAIQjRxqFwoTCJj8kIPsvugCFQAAAAAdAAAAABAD'}
alt="card image"
style={{width:"100%",height:360}}/>
:
<img src ={`http://image.tmdb.org/t/p/w185${props.image}`}
alt="card image"
style={{width:"100%",height:360}}/>
}
<div className="card-content">
<p>
</p>
</div>
</div>
</div>
</div>
The movieLists component lists the movie component on top of each other, i am trying to find the way to list them horizontally responsively.
Remember to always use the correct grid structure, which is:
.container > .row > .col
All cols MUST sit in rows to behave responsively. Adding a .row div around the movie.js output will fix your problem.
<div class="container">
<div class="row">
<div class="col">
[content goes here]
</div>
<div class="col">
[content goes here]
</div>
</div>
</div>
See this answer from yesterday:
Materialize grid s12 not working in mobile view or on Chrome's developer tools
Codepen with a single .row div to fix your problem (note I've had to chnage className to class):
https://codepen.io/doughballs/pen/eYNbwOQ
https://materializecss.com/grid.html

v-for logic in vuejs for dynamic image grid

So I kept sketching out algorithms to see how this would work. Kept hitting my head on the keyboard because frankly, nothing seems to work. Basically the hardcoded HTML looks like this:
<div class="row">
<div class="column">
<a href="products.html#drinks"><div id="drinks">
<h2>Drinks</h2>
</div></a>
</div>
<div class="column">
<a href="products.html#preppedfood"><div id="preppedfood">
<h2>Prepped Food</h2>
</div></a>
</div>
<div class="column">
<a href="products.html#coffee"><div id="coffee">
<h2>Coffee Machines</h2>
</div></a>
</div>
<div class="column">
<a href="products.html#snacks"><div id="snacks">
<h2>Snacks</h2>
</div></a>
</div>
</div>
<div class="row">
<div class="column">
<a href="products.html#nuts"><div id="nuts">
<h2>Nuts of All Kinds</h2>
</div></a>
</div>...till the last div class="row" and it's column divs
So the HTML works fine, here's a screenshot:
This is how I want it to look!
But using VueJS so that I can "DRY out" my markup, I'm using the v-for directive like this in the Prods.vue component. Here's the template markup:
<div class="row" v-for="(data, index) in images" v-bind:key="data" v-if="data.ImageId%4 == 0">
<h1>{{index}}</h1>
<div
class="column"
v-for="data in images"
v-bind:key="data"
v-if="computedIndex(index) *4 >= index && index < (computedIndex(index)+1) / 4"
v-lazy-container="{ selector: 'data' }"
>
<a :href="'products/#'+data.Name">
<h4>{{data.H2}}</h4>
<img :src="'../../../static/products/'+data.Name+'.png'" :alt="data.Name+'.png'" />
</a>
</div>
</div>
And the script:
<script>
import Prods from '../../../other/jsons/products.json'
import VueLazyload from 'vue-lazyload'
export default {
data() {
return {images: Prods}
},
methods: {
computedIndex(index) {
return Math.trunc(index/4)
}
}
}
//v-for in row
//v-for in column
</script>
And this is what shows up instead:
enter image description here
Instead of juggling with indices, it seems more straightforward to me to compute the shape of your array to suit your DOM:
computed:
imageRows () {
return this.images.reduce((acc, n, i) => {
i % 4 ? acc[acc.length - 1].push(n) : acc.push([n])
return acc
}, [])
}
To be used something like this:
<table>
<tr v-for="(imageRow, i) in imageRows" :key="i">
<td v-for="image in imageRow" :key="image">
<foo/>
</td>
</tr>
</table>
(Your data seems tabular to me, so I'm showing this example as a <table>, but you can substitute that with <div>'s if you prefer, of course.)

How I can take data from a json inside a ng-init in the view

I have a json inside a ng-init where I have some data and I want to grab these data and paint into a template and I compiled with my directive and I don't have idea what I'm doing wrong. I can not paint json data into the template
example:
<li ng-init="product= {"brand":"Nike", "price":"30€", "mainImage":"images/images.jpg"}>
<div class="content-product>
<div class="product">
<p>Nike </p>
<img src="image.jpg" alt="">
<p>30€ </p>
</div>
</div>
</li>
This li is inside a loop in ruby.
Template where I want to paint the data and compile this template when I click on a button that is inside the li:
<script type="text/ng-template" id="quickpreview.html">
<div class="content-preview">
<div class="content-preview-inner">
<span class="full-preview"></span>
<span class="close-preview"></span>
<div class="block block-left left">
<div class="content-tabs">
<dl class="tabs vertical" data-tab>
<dd class="active">Tab 1
</dd>
<dd>Tab 2
</dd>
<dd>Tab 3
</dd>
<dd>Tab 4
</dd>
<dd>Tab 5
</dd>
</dl>
<div class="tabs-content vertical">
<div class="content active" id="panel1">
<div class="content-img">
<div class="main-img">
<img ng-src="{{product.mainImage}}" alt="">
</div>
<div class="thumbnails">
<a class="th" role="button" aria-label="Thumbnail" href="">
<img aria-hidden=true src="" />
</a>
</div>
</div>
</div>
<div class="content" id="panel2">
<p>This is the second panel of the basic tab example. This is the second panel of the basic tab example.</p>
</div>
<div class="content" id="panel3">
<p>This is the third panel of the basic tab example. This is the third panel of the basic tab example.</p>
</div>
<div class="content" id="panel4">
<p>This is the fourth panel of the basic tab example. This is the fourth panel of the basic tab example.</p>
</div>
<div class="content" id="panel5">
<p>This is the fifth panel of the basic tab example. This is the fourth panel of the basic tab example.</p>
</div>
</div>
</div>
</div>
<div class="block block-right right">
<div class="content-details">
<div class="details">
<h3 class="title-product">{{product.brand}}</h3>
<h2 class="short-desc">{{product.shortname}}</h2>
<div class="block-price">
</div>
</div>
</div>
</div>
</div>
</div>
my directive and controller:
(function() {
'use strict';
var app = angular.module('quickPreview');
app.controller('globalCtrl', function ($scope) {
// var e = angular.element($("[ng-init]"));
// console.log(e);
// $scope.product = e.attr('ng-init');
// console.log($scope.product);
$scope.product = [];
var logSomeStuff = function(){
console.log($scope.product);
}
$scope.$evalAsync(logSomeStuff);
});
}(window, window.angular));
(function (){
"use strict";
var app = angular.module('quickPreview');
app.directive('previewProduct', function ($compile,$templateCache) {
return {
restrict: 'A',
replace: false,
transclude: false,
scope: {
attrData: '=dataOverview'
},
link: function(scope, element, attrs) {
element.bind('click', '.sd-click-preview', function (){
var preview = angular.element($templateCache.get('quickpreview.html'));
var cpreview = $compile(preview);
element.append(preview);
cpreview(scope);
console.log(cpreview(scope))
if (scope.attrData) {
console.log(this, '=> this');
}
});
}
};
});
}(window, window.angular));
You don't need quotes in ng-init statement. You are ending the statement prematurely.
<li ng-init="product= {brand:'Nike', price:'30€', mainImage:'images/images.jpg'}">
<div class="content-product">
<div class=" product ">
<p>{{product.brand}}</p>
<img ng-src="{{product.mainImage}} " alt=" ">
<p>{{product.price}}</p>
</div>
</div>
</li>
Here's a working example:http://plnkr.co/edit/XRyYefxncZuqE5GeA3XG?p=preview

Three equal sized buttons in Bootstraper

I've started to use bootstraper. And I want a row with three buttons. The buttons should have the same height and width. How can I achieve this?
I have come up with the following, but this gives me different heights of the buttons.
<div class="row-fluid">
<div class="span2 offset1">
<a href="#" class="btn btn-info btn-block">
<div class="buttonbody">
<img src=".." />
<p>Button1<br />Second row<p>
</div>
</a>
</div>
<div class="span2 offset1">
<a href="#" class="btn btn-info btn-block">
<div class="buttonbody">
<img src=".." />
<p>Button2<p>
</div>
</a>
</div>
<div class="span2 offset1">
<a href="#" class="btn btn-info btn-block">
<div class="buttonbody">
<img src=".." />
<p>Button3<p>
</div>
</a>
</div>
</div>
To get the same height, you'll have to use jQuery to calculate the max then apply it to all the elements that should have the same height:
var selector = ".row-fluid .buttonbody";
var maxHeight = 0;
$(selector).each(function() {
var selfHeight = $(this).height();
if (selfHeight > maxHeight) {
maxHeight = selfHeight;
}
});
// set the same height on all elements
$(selector).height(maxHeight);
UPDATE: To resize buttons each time the window is resized, you can do the following:
// declare a function to be called
// each time buttons need to be resized
var resizeButtons = function() {
var selector = ".row-fluid .buttonbody";
var maxHeight = 0;
$(selector).each(function() {
var selfHeight = $(this).height();
if (selfHeight > maxHeight) {
maxHeight = selfHeight;
}
});
// set the same height on all elements
$(selector).height(maxHeight);
}
$(function() {
// attach function to the resize event
$(window).resize(resizeButtons);
// call function the first time window is loaded;
resizeButtons();
});
Hope that helps.
Short Answer : http://jsfiddle.net/D2RLR/2942/
Long Answer:
The following code does what you want.
<div class="container">
<strong>Button 1</strong>
<strong>Button 2</strong>
<strong>Button 3</strong>
</div>​
Here is tutorial for the same.
I recommend you go through twitter bootstrap documenation and bootsnipp.com for more information.
From your comments, as you say you are using <br/> you can use the following : fiddle,
<div class="container">
<strong>Button 1<br/>Second row</strong>
<strong>Button 2<br/> </strong>
<strong>Button 3<br/> </strong>
</diV>​