I would like to do the following. I would like to create a categories page that loads each category onto a picture. Then when a user clicks a category the parameter would show up on the url and it will search the database for photos based on that category. I will now demonstrate the code that I have.
Categories HTML File:
<template name="categories">
<div class="text-center light-container" id="categories-section">
<h1>Categories</h1>
<hr/>
{{#each categories}}
<div class="image-container">
<a href="{{pathFor route='categoryPage'}}">
<img class="freezeframe pics" src="images/app/sphere.jpg"/>
</a>
<h2><span>{{categories}}</span></h2>
</div>
{{/each}}
</div>
</template>
Categories JS File:
var categoryList = ['actions', 'animals', 'anime', 'art/design', 'cartoons/comics', 'celebrities',
'Emotions', 'Food/Drink', 'Games', 'History', 'Holidays', 'Memes', 'Movies', 'Music', 'Nature', 'News/Politics',
'Science', 'Sports', 'Technology', 'TV'];
Template.categories.helpers({
'categories':function(){
for(var i = 0; i < categoryList.length; i++){
console.log(categoryList[i]);
return categoryList[i];
}
}
});
Router JS File:
Router.route('/categories', {
name: 'categories',
template: 'categories',
fastRender: true
});
Router.route('/categories/:category', {
name: 'categoryPage',
template: 'categoryPage',
data: function(){
var category = this.params.category;
return GifsCollection.find({category: category});
},
fastRender: true
});
CategoryPage JS:
Template.categoryPage.helpers({
// Load 16 most recent ones
// When down arrow is click load another 16 more gifs
gifs: function() {
var category = this.params.category;
return GifsCollection.find({category: category}, {fields: {_id: 1, image: 1} });
}
});
Running the following code doesn't get me anywhere. I'm not sure which path to take. I can create a 'category' collection and load the helpers onto there or I can use sessions? but I'm leaning onto creating category collection however I'm pretty sure there is a much more efficient way.
Any feedback and code help would be greatly appreciated!!!
You have two small errors. One we already discussed in comments, regarding the helper. The other is in how you use the #each loop: inside of it you can just refer to this to get the string of your category.
Categories HTML File:
<template name="categories">
<div class="text-center light-container" id="categories-section">
<h1>Categories</h1>
<hr/>
{{#each categories}}
<div class="image-container">
<a href="/categories/{{this}}">
<img class="freezeframe pics" src="images/app/sphere.jpg"/>
</a>
<h2><span>{{this}}</span></h2>
</div>
{{/each}}
</div>
</template>
Categories JS File:
var categoryList = ['actions', 'animals', 'anime', 'art/design', 'cartoons/comics', 'celebrities',
'Emotions', 'Food/Drink', 'Games', 'History', 'Holidays', 'Memes', 'Movies', 'Music', 'Nature', 'News/Politics',
'Science', 'Sports', 'Technology', 'TV'];
Template.categories.helpers({
'categories':function(){
return categoryList;
}
});
Related
I have a main page and there are items on the page. When I click "Order Now" on an item, an HTML page slides in over a portion of the page to show the item's details. I am having trouble linking the item clicked with an item inside an array of objects containing the details for each item so I can show the details on the page that slides over.
Here's the main page:
<div class="col-md-4 col-sm-6">
<div class="shop-item">
<div class="shop-thumbnail">
<img src="img/originalImage.jpg" alt="">
</div>
<div class="shop-item-footer">
<div class="shop-item-details">
<h3 class="shop-item-title">Original Title</h3>
<span class="shop-item-price">
$50.00
</span>
</div>
<div class="shop-item-order-btn">
Order Now
</div>
</div>
</div>
</div>
The page slides over fine, but I can't seem to get the image to change. The html file that has the info that slides out onto the screen is all encompassed into an article tag. Here's the top snippet of the HTML with the image that I can't seem to get to change.
<article class="order-details">
<div class="order-totals">
<div class="product-image">
<!-- ID for image -->
<img id="pageOrder" src="../img/Image1.jpg" alt="">
</div>
</div>
</article>
This is my JS so far that's not working.
var items = [{ name: 'Black', cost: '$10.00', id: '001', src: '../img/Black.jpg' }, { name: 'Something', cost: '$10.00', id: '002', src: '../img/image2.jpg' },
function changeImage() {
for (var i = 0; i >= items.count; i++) {
if (i = items.count) {
document.getElementById('pageOrder').src = items.src;
}
}
};
If I understand you correctly, when you click the order button, you can find the item by id in items by reading the data-id property on the order button that was clicked and then access the properties from that item like so:
To get this working with your specific scenario, you will have to modify some functions that you did not include in your original question, but I dug up from the link you provided in the comments. The code would look like the following:
var items = [{
name: 'Black',
cost: '50.00',
id: '001',
src: 'img/Black2.jpg'
}, {
name: 'Red',
cost: '50.00',
id: '002',
src: 'img/BloodRed2.jpg'
}, {
name: 'Desert Fox',
cost: '50.00',
id: '003',
src: 'img/DesertFox.jpg'
}];
// Single Post via Ajax
//------------------------------------------------------------------------------
var ajaxLoadLink = $('.ajax-load-link'),
orderBackdrop = $('.order-content-backdrop'),
orderContainer = $('.order-content-wrap'),
orderContentWrap = $('.order-content-wrap .inner'),
orderContent = $('.order-content-wrap .inner .order-content'),
closeBtn = $('.order-content-wrap .close-btn'),
orderPreloader = $('.order-content-wrap .preloader');
// Open Order
function openOrder(itemDetails) {
$('body').addClass('order-content-open');
orderBackdrop.addClass('active');
orderContainer.addClass('open');
orderPreloader.addClass('active');
orderContentWrap.addClass("loaded");
setTimeout(function() {
orderPreloader.removeClass('active');
// insert data from itemDetails into their respective places on the form
$(".order-content-wrap .order-details .product-image img").attr("src", itemDetails.src);
$(".order-content-wrap .order-details .product-image img").attr("alt", itemDetails.name);
$(".order-content-wrap .product-title").text(itemDetails.name);
$(".order-content-wrap .price i").text(itemDetails.cost);
$(".order-content-wrap .total-cost i").text(itemDetails.cost);
}, 900);
}
// Close Order
function closeOrder() {
$('body').removeClass('order-content-open');
orderBackdrop.removeClass('active');
orderContainer.removeClass('open');
orderContentWrap.removeClass('loaded');
}
ajaxLoadLink.on('click', function(e) {
var itemDetails = items.find(item => item.id === $(this).attr('data-id'));
openOrder(itemDetails);
e.preventDefault();
});
Working Demo
Code is too long to put in a stack snippet
Make sure to press "Run with JS" when you load the jsbin
Im trying to create a blog(with meteor) that will have different categories for posts, then trying to create a page that displays all categories and the titles off posts in those categories.
This is the Javascript code I am using.
Template.categories.cats = function(){
reviews = Reviews.find({}, {sort: { createdAt: -1 } });
opinions = PointlessOpinions.find({}, {sort: { createdAt: -1 } });
days = DaysInTheLife.find({}, {sort: { createdAt: -1 } });
return {reviews: reviews,opinions: opinions, days: days};
}
This is the HTML template
<template name = "categories">
<div class = "container">
<h1>Reviews</h1>
{{#each reviews}}
<h2> {{title}}</h2>
{{/each}}
</div>
<div class = "container">
<h1>A day in the life</h1>
{{#each days}}
<a href="/post/{{this._id}}">
<h2> {{title}}</h2>
</a>
{{/each}}
</div>
<div class = "container">
<h1>Pointless Opinions</h1>
{{#each opinions}}
<a href="/post/{{this._id}}">
<h2> {{title}}</h2>
</a>
{{/each}}
</div>
</template>
I have tested to see if the Collections have the data and it seems so
You should write it like this:
Template.categories.helpers({
reviews: function (){
return Reviews.find({}, {sort: { createdAt: -1 } });
},
//and the rest of the helpers to use in that template
});
Luna is correct and you should not do .fetch() as Ramil suggested. You are allowed to return a cursor from a helper and you should do so as it is more performant than returning an Array.
I am learning inherited/isolated scopes in angular directives and struck with some basic concepts. please see the plunker below.
Example.
Scenario1:
I have 2 directives (book and details). I am displaying two "Book Details" containers and toggling book name by sending custom attributes like this.
<book bookdetails="book" collapsed="yes" list="list"></book>
<book bookdetails="book1" collapsed="no" list="list"></book>
Question: Is this the right way to handle displaying things in 2 different containers?
Scenario 2:
I want to hide the author details section in container 1 but show in container2 on load. How to accomplish that?
When I use this line below it will hide and show both author details section but I want to keep it separate.
<details collapsed="yes"></details>
I know I am lacking basic skills using inherited/isolated scopes. Can someone educate me?
It's OK to use nested directives like you've used so you can do everything related to the details pane in the details controller like removing items from the books list.
If you wouldn't do any logic in details controller and just include some html I would just use ng-include.
Some points I've detected during improving your code:
Template markups are partial html files, so no need to add header, body etc. Just add your markup that you need in your directive.
I've created one model array books that you can iterate with ng-repeat and not two separate scope variables. That's easier to add more books.
I wouldn't pass the collapsed state to directive isolated scope. I would add it to the book model then you can have independent states of the details panes.
You could also create a collapsed array scope variable separate from your model and use it like ng-hide='collapsed[$index]' if you don't like to add it to your model.
Don't compare to the string yes. It makes things more complicated. It's better to use true or false.
The list you're passing is OK if you'd like to use one list for every details pane. But I think you need them independent from each other so add it to your book model.
For toggeling a value you can use the js shorthand: collapsed = !collapsed;. It takes the value of collapsed and inverts it and re-asigns it to collapsed.
Details directive: You don't need to pass attributes to a directive that doesn't use isolated scope. Instead you can directly use the inherited scope of the parent.
Note: I think you should have a look at angular-ui-bootstrap and use an accordion instead of your manually created panes later. But for learning directives your code is OK.
Please have a look at your updated code below or in this plunker.
If something is not clear, feel free to add a comment and I'll try to help.
angular.module('plunker', [])
.controller('MainCtrl', function($scope) {
$scope.books = [{
id: 0,
name: 'Building modern ASP.NET 5',
author: {
name: 'name1',
age: 31,
country: 'USA'
},
collapsed: false,
list: [{
id: 0,
name: 'book1'
}, {
id: 1,
name: 'book2'
}, {
id: 2,
name: 'book3'
}]
}, {
id: 1,
name: 'AngularJS',
author: {
name: 'name2',
age: 27,
country: 'USA'
},
collapsed: true,
list: [{
id: 0,
name: 'book1'
}, {
id: 1,
name: 'book2'
}, {
id: 2,
name: 'book3'
}]
}];
//$scope.list = ["book1", "book2", "book3"];
}).directive('book', function() {
return {
restrict: 'E',
templateUrl: 'book.html',
scope: {
bkdet: "=bookdetails"
//list: "="
//collapsed: "#"
},
controller: function($scope) {
$scope.toggleDetails = function() {
$scope.bkdet.collapsed = !$scope.bkdet.collapsed;
updateCaption();
};
function updateCaption() {
$scope.hypshowhide = $scope.bkdet.collapsed ? 'show details' : 'hide details';
}
// first run
updateCaption();
/*if ($scope.collapsed == 'yes')
{
$scope.dethide = true;
}
else {
$scope.dethide = false;
} */
//$scope.hypshowhide = 'show details';
}
}
})
.directive('details', function() {
return {
restrict: 'E',
templateUrl: 'details.html',
controller: function($scope) {
/*console.log($scope.bkdet.collapsed);
if (!$scope.bkdet.collapsed) { //== 'yes') {
$scope.dethide = true;
}
else {
$scope.dethide = false;
}*/
$scope.removeItem = function(index) {
$scope.bkdet.list.splice(index, 1);
}
}
}
})
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="plunker">
<div ng-controller="MainCtrl">
<div class="container">
<book bookdetails="book" ng-repeat="book in books"></book>
</div>
</div>
<script type="text/ng-template" id="book.html">
<div class="row">
<div class="panel panel-default">
<div class="panel-heading">
<h1>Book Details</h1>
</div>
<div class="panel-body">
<a class="pull-right" href="#" ng-click="toggleDetails(collapsed)">{{hypshowhide}}</a>
<div>
<!--ng-hide="dethide">-->
{{bkdet.name}}
</div>
<!--<details collapsed="no"></details>-->
<details></details>
</div>
</div>
</div>
</script>
<script type="text/ng-template" id="details.html">
<div class="container" ng-hide="bkdet.collapsed">
<div class="row">
<ul class="list-group list-unstyled">
<!--<li>
<h1>Author:</h1>
</li>
<li>
<ul>-->
<li>
<strong>Author</strong>
{{bkdet.author.name}}
</li>
<li>
<strong>Age</strong>
{{bkdet.author.age}}
</li>
<li>
<strong>Country</strong>
{{bkdet.author.country}}
</li>
<li>
<div ng-if="bkdet.list.length == 0">
<p>No books here!</p>
</div>
<div ng-repeat="c in bkdet.list">
<p>
{{c.name}}
<button class="btn btn-danger" ng-click="removeItem($index)">X</button>
</p>
</div>
</li>
<!--</ul>
</li>-->
</ul>
</div>
</div>
</script>
</div>
I'm working on a meteor project where I want to place an objects from the same collection in different divs depending on their properties. The easiest way to explain is probably to show a test case:
html
<template name="board">
{{#each rows}}
<div id="row-{{this}}" class="row">
{{#each columns}}
<div id="{{..}}-{{this}}" class="column column-{{this}} row-{{..}}- column">
{{>pins }}
</div>
{{/each}}
</div>
{{/each}}
</template>
<template name="pins">
{{#each pins}}
<div class = "pin" >{{this}}</div>
{{/each}}
</template>
js
Template.board.helpers({
rows: [
'top',
'middle',
'bottom'
],
columns: [
'left',
'center',
'right'
]
});
Template.pins.helpers({
pins:[
{name: 'test1', loaction: 'bottomcenter'},
{name: 'test2', loaction: 'topleft'},
{name: 'test3', loaction: 'bottommcenter'},
{name: 'test4', loaction: 'middleright'}
]
});
I'd like to place the pins in the correct div based on their location. Now I can of course manually write out every div in the html and a helper for each one (and will if there's no better solution), but I'm trying to figure out what the most efficient solution is.
I tried passing the location back to the helper function with the following code:
{{#each pins location="{{..}}{{this}}}}
and this
{{#each pins location="{{..}}{{this}}"}}
and running a function, But the tags after location= come through as {{..}}{{this}} instead of the values.
I also tried restructuring the data like this:
pins:{
bottomcenter: [{name: 'test1'}, {name: 'test3'}]
topleft:[{name: 'test2'}]
}
etc, and passing the parameter as a data context:
{{>pins {{..}}{{this}}}}
but that didn't seem to work either. Any help is appreciated!
your template should like:
<template name="pins">
{{#each pins}}
<div class = "pin" >{{this.name}} {{this.location}}</div>
{{/each}}
</template>
and the helper like this:
Template.pins.helpers({
pins: function() {
return [
{name: 'test1', loaction: 'bottomcenter'},
{name: 'test2', loaction: 'topleft'},
{name: 'test3', loaction: 'bottommcenter'},
{name: 'test4', loaction: 'middleright'}
]
}
});
I think I got it! The trick seems to be that you can't embed {{}} brackets in other {{}} brackets. So you use:
<template name="pins">
{{#each pins .. this}}
<div class="pin">
{{this.name}}
</div>
{{/each}}
</template>
.. and this get passed back to the helper function, and then you can search based on the results:
Template.pins.helpers({
pins: function(row,col){
var search = row+"-"+col;
var results = [];
var pinarr = [
{insert data here}
];
for(var i = 0; i < pinarr.length ; i++){
if(pinarr[i].location === search) {
results.push(pinarr[i]);
}
}
return results;
}
});
}
I have a few questions. The first is why can't I display data without putting 'in model' when using the each helper? Also I'm having issues showing products specific to the category I'm in?
View an image of my inspect here:
Currently when I click on a category, it shows all of my products.
I want to show all of my categories, then list my products associated to the category I clicked ( I need these available at all times). Shouldn't I be able to just do the following?
<ul class="off-canvas-list">
<li><label>Solution Category</label></li>
{{#each category}}
<li class="has-submenu">
{{name}}
<ul class="left-submenu">
<li class="back">Back</li>
{{#each products}}
{{name}}
{{/each}}
</ul>
</li>
{{/each}}
</ul>
Router.js
App.Router.map(function () {
this.route('index', { path: '/' });
this.route('line-of-one');
this.route('operating-room');
this.route('sterile-processing');
this.route('solution');
this.route('department');
this.route('category');
this.resource('products');
this.resource('presentations', { path: '/presentations' }, function () {
// this one is nested and dynamic, we need it to see one user at a time with its id
this.resource('presentation', { path: '/:presentation_id' }, function () {
// another nested one for editing the current user
this.route('edit');
});
// and a last one to create users
this.route('create');
});
// this is our 404 error route - see MissingRoute just bellow
this.route('missing', { path: '/*path' });
});
// this handles wrong routes - you could use it to redirect to a 404 route or like here to redirect to the index page
App.MissingRoute = Em.Route.extend({
redirect: function () {
this.transitionTo('index');
}
});
Application Route
App.ApplicationRoute = Ember.Route.extend({
model: function () {
return this.store.findAll('category')
},
setupController: function (controller, model) {
this._super(controller, model);
controller.set('product', this.store.find('product'));
}
})
Products Route
App.ProductsRoute = Ember.Route.extend({
model: function () { return this.store.find('product') }
})
Category Model
App.Category = DS.Model.extend({
name: DS.attr('string'),
room: DS.attr('string'),
subroom: DS.attr('string'),
products: DS.hasMany('product')
});
App.Category.FIXTURES = [
{
id: 0,
room: "Operating Room",
subroom: "Operating Room",
image: "Content/Images/Products/Case-Goods.png",
name: "Case Goods",
rendertemplate: "sliderTemplate",
sort: 10,
products: [0]
}
]
Product Model
App.Product = DS.Model.extend({
name: DS.attr('string'),
room: DS.attr('string'),
subroom: DS.attr('string'),
image: DS.attr('string'),
category: DS.belongsTo('category')
});
App.Product.FIXTURES = [
{
id: 0,
room: "Operating Room",
subroom: "Operating Room",
category: "Case Goods",
image: "Content/Images/Products/Case-Goods.png",
name: "Case Goods Solutions",
content: "Innovations to reduce procedural delays and keep everything at your fingertips.",
bullets: [
{ content: "Instant access to supplies and equipment to help minimize procedural delays" },
{ content: "Protection of your sterile supplies behind closed doors with durable, easy-to-clean surfaces and either stainless steel or tempered glass door fronts" },
{ content: "Select freestanding or recessed consoles to fit your space" }
],
sellsheet: "Content/Sellsheets/casegoods.html",
rendertemplate: "valuePropTemplate",
sort: 0
}
]
My Navigation
<ul class="off-canvas-list">
<li><label>Solution Category</label></li>
{{#each category in model}}
<li class="has-submenu">
{{category.name}}
<ul class="left-submenu">
<li class="back">Back</li>
{{#each product in model}}
{{{product.name}}}
{{/each}}
</ul>
</li>
{{/each}}
</ul>
1.The context of your the handlebars is the controller, if you used the snippet below it would work
{{#with model}}
{{each product}}
...
{{/each}}
{{/with}}
i see you have not implemented a custom route handler for /products so by default ember will assume that you are looking for all of the products. which makes sense as the url doesnt have any information about the selected category. you could nest product within category, or you could filter the results on the /products route using Query Paramenters
I'm not sure exactly how to explain why this worked but: I was passing my model data to the ApplicationRoute so I didn't need to specify a model to each through. I understand that if your in a view associated with a route and you just use {{#each}} the context will be the data your passing through? The code below ended up working. If someone good provide an explanation that would be greatly appreciated!
<ul class="off-canvas-list">
<li><label>Solution Category</label></li>
{{#each}}
<li class="has-submenu">
{{name}}
<ul class="left-submenu">
<li class="back">Back</li>
{{#each products}}
{{{name}}}
{{/each}}
</ul>
</li>
{{/each}}
</ul>