"Yield" doesn't work in binding.scala - scala

Several days ago I read about binding.scala and I found it so cool therefore I decided to write my own single page app.
The problem is that I'm trying to add "li" items into "ul" element, but it seems like the component Want doesn't see updates.
The code below:
case class Movie(title: Var[String], raring: Var[Int], watched: Var[Boolean])
var movies = Vars.empty[Movie]
#dom def Want = {
println(movies.bind, "!##!##!##!")
<div class="want">
<ul>
{for (movie <- movies.value) yield {
<li>
<div>
<span>
{movie.title.bind}
</span>
<button onclick={event: Event => {
event.preventDefault()
movies.value.remove(movies.value.indexOf(movie))
println(movies.value)
}}></button>
</div>
</li>
}}
</ul>
</div>
When I change movies nothing happens.
UPDATE
After the comment below I updated the code:
def remove(movie:Movie) = {
movies.value.-=(movie)}
#dom def Want = {
println(movies, "!##!##!##!")
<div class="want">
<ul>
{for (movie <- movies.bind) yield {
<li>
<div>
<span>
{movie.title.bind}
</span>
<button onclick={event: Event => {
event.preventDefault()
remove(movie)
}}></button>
</div>
</li>
}}
</ul>
</div>
}
However, the code doesn't work.

Please change for (movie <- movies.value) to for (movie <- movies).
According to the Scaladoc of value method:
Note: This method must not be invoked inside a #dom method body.

Related

Template not reactive in meteor on adding new data?

This are the event listeners for my two templates,specified in events.js
// event listeners on the addSiteForm template
Template.addCommentForm.events({
// this runs when they click the add button... you need to compete it
'click .js-add-comment':function(event){
var comment_text = $('#comment_input').val();// get the form value using jquery...
var user = 'anonymous person';
// the 'this' variable contains
// the data that this template is displaying
// which is the Website item
var site = this;
if (Meteor.user()){
user = Meteor.user().emails[0].address
}
var comment = {"text":comment_text,
"siteId":site._id,
"createdOn":new Date(),
"createdBy":user};// create a simple object to insert to the collectoin
Comments.insert(comment);
console.log("events start");
console.log("totla coomets are "+Comments.find({}).count()+"\n");
console.log("commenst in site "+Comments.find({siteId:site._id}).count()+"\n");
console.log("site id is "+site._id);
console.log("events end");
return false;
}
});
// event listeners on the addSiteForm template
Template.addSiteForm.events({
// this runs when they click the add button... you need to compete it
'click .js-add-site':function(event){
var url = $('#url_input').val();// get the form value using jquery...
var user = 'anonymous person';
if (Meteor.user()){
user = Meteor.user().emails[0].address
}
var site = {"url":url,
"createdOn":new Date(),
"createdBy":user};// create a simple object to insert to the collectoin
Websites.insert(site);
return false;
}
});
The router function is specified in router.js
Router.configure({
layoutTemplate: 'ApplicationLayout'
});
// the main route. showing the list of sites.
Router.route('/', function () {
this.render('siteList');
});
// this route is for the discussion page for a site
Router.route('/discussSite/:_id', function () {
var siteId = this.params._id;
site = Websites.findOne({_id:siteId});
this.render('discussSite', {data:site});
});
the main.js contain the collections
// shared code
Websites = new Mongo.Collection("websites");
Comments = new Mongo.Collection("comments");
helpers is
// this helper gets the data from the collection for the site-list Template
Template.siteList.helpers({
'all_websites':function(){
return Websites.find({});
},
'safer_email':function(email){
if (email.indexOf('#')!=-1){// we have an email
return email.split('#')[0];
}
else{// probably anonymouse.
return email;
}
}
});
Template.discussSite.helpers({
'comments':function(siteId){
//console.log("helper comments "+this.site);
// complete the code here so that it reruns
console.log(this.params.site._id);
return Comments.find({siteId:siteId});
// all the comments with a siteId equal to siteId.
}
});
the html file is in main.html.here it first displays the websites available in the webpage.On clicking this the user is routed to a new page.Here the user can add comments for the website
<head>
<title>week_4_peer_assessment</title>
</head>
<body>
</body>
<!-- this is the template that iron:router renders every time -->
<template name="ApplicationLayout">
<div class="container">
Home
{{>loginButtons}}
<h1>SiteAce - discuss your favourite websites</h1>
<!-- iron router will select what to render in place of yield-->
{{> yield }}
</div>
</template>
<template name="discussSite">
<h3>Discussing: {{url}} </h3>
{{> addCommentForm}}
<!-- write some code here that iterates through the comments and displays
the comment text and the author -->
<!-- clue - you have already written the 'comments' helper function -->
<ul>
{{#each comments}}
<li>{{text}} (added by {{safer_email createdBy}})
</li>
{{/each}}
</ul>
</template>
<template name="addCommentForm">
<div class="row">
<div class="col-lg-6">
<div class="input-group">
<input type="text" id="comment_input" class="form-control" placeholder="Enter your comment">
<input type="hidden" id="site_id" class="form-control" placeholder="Enter your comment">
<span class="input-group-btn">
<button class="btn btn-default js-add-comment" type="submit">Add!</button>
</span>
</div><!-- /input-group -->
</div><!-- /.col-lg-6 -->
</div>
</template>
<template name="siteList">
Fill in the form and click submit to add a site:
{{>addSiteForm}}
<h3>Sites you have added:</h3>
<ul>
{{#each all_websites}}
<li>{{url}} (added by {{safer_email createdBy}})
<br/>discuss
<br/>visit site
</li>
{{/each}}
</ul>
</template>
<template name="addSiteForm">
<div class="row">
<div class="col-lg-6">
<div class="input-group">
<input type="text" id="url_input" class="form-control" placeholder="Enter website URL...">
<span class="input-group-btn">
<button class="btn btn-default js-add-site" type="submit">Add!</button>
</span>
</div><!-- /input-group -->
</div><!-- /.col-lg-6 -->
</div>
</template>
The starup.js contains the start up code run by the server
Meteor.startup(function(){
if (!Websites.findOne()){// nothing in the database yet
var site = {"url":"http://www.google.com",
"createdOn":new Date(),
"createdBy":"Michael"};// create a simple object to insert to the collectoin
Websites.insert(site);
site = {"url":"http://www.yeeking.net",
"createdOn":new Date(),
"createdBy":"Janet"};// create a simple object to insert to the collectoin
Websites.insert(site);
site = {"url":"http://www.coursera.org",
"createdOn":new Date(),
"createdBy":"Jose"};// create a simple object to insert to the collectoin
Websites.insert(site);
}
});
This is what your template should be: (I removed safer_email. It was erroring. Not sure if it was something else you were working on.)
<template name="discussSite">
<h3>Discussing: {{url}} </h3>
{{> addCommentForm}}
<!-- write some code here that iterates through the comments and displays
the comment text and the author -->
<!-- clue - you have already written the 'comments' helper function -->
<ul>
{{#each comments}}
<li>{{text}} (added by {{createdBy}})</li>
{{/each}}
</ul>
</template>
helper:
your helper function is designed to take in siteId as an input, yet you are not passing one to it.
this._id or Router.current().params._id will get you the siteId that you are looking for. You'd used this.site._id which used to error out as this.site was not valid.You can further see what the this object contains by doing a console.log(this). That would have helped you sort this out faster.
'comments':function(){
console.log("site id in helper is ",this._id); // or Router.current().params._id;
console.log(Comments.find({siteId:this._id}).fetch());
return Comments.find({siteId:this._id});
}

Show tags under their own specific tab

I'm creating a tag system on my website. So far it's all working great. But now I wish to create a page that shows all my tags under their own tab like you have with a portfolio page.
Example: http://www.don-zalmrol.be/tags?tag=Electronics
My page already displays the tags for a specific tag under it's own tab (i.e. electronics), but as you might guess I wish to populate the other tags in their respective tab as well.
So in short you land a view that displays the tag you've selected, but on the same page you can see the others as well.
Anybody has any idea how I can do this? I don't think I'm far away from the solution as I can already load the projects for specific tags under it's own tab. Now I only need to populate the remaining tabs with the tags!
Thanks!
This is my code so far:
http://pastebin.com/jwGW0NKZ
#inherits Umbraco.Web.Mvc.UmbracoTemplatePage
#inherits Umbraco.Web.Mvc.UmbracoTemplatePage
#{
var portfolio = Umbraco.TagQuery.GetAllContentTags().OrderBy(t => t.Text);
var tagList = Umbraco.TagQuery.GetAllContentTags().OrderBy(t => t.Text);
string tag = Request.QueryString["tag"];
if (!tag.IsNullOrWhiteSpace())
{
var publishedContent = Umbraco.TagQuery.GetContentByTag(tag);
if (publishedContent.Count() > 0)
{
#* Show title *#
<div class="media contact-info wow fadeInDown" data-wow-duration="1000ms" data-wow-delay="600ms">
<center>
<div>
<i class="fa fa-tags"></i>
</div>
<br />
<div class="media-body">
<h2>Tags</h2>
<p>Browse content by tag</p>
</div>
</center>
<br />
</div>
#* Show tag titles in tabs *#
<ul class="portfolio-filter text-center">
<li><a class="btn btn-default" href="#" data-filter="*">All tags</a></li>
#foreach (var tags in tagList)
{
<!-- Create a selected tag -->
if(#tags.Text == #tag)
{
<li><a class="btn btn-default active" href="#" data-filter=".#tag">#tag</a></li>
}
#* Show all other tags *#
else
{
<li><a class="btn btn-default" href="#" data-filter=".#tags.Text">#tags.Text</a></li>
}
}
</ul>
<div class="row">
<div class="portfolio-items">
#* Start picture content *#
#foreach (var tags in tagList)
{
#* Put selected tag in the right tag tab *#
if(#tags.Text == #tag)
{
#* Show tag content *#
foreach (var item in publishedContent.OrderByDescending(i => i.CreateDate))
{
<div class='portfolio-item #tag col-xs-12 col-sm-4 col-md-3'>
<div class="recent-work-wrap">
#* IF the project has a picture *#
#if(item.HasValue("pictureOfTheProject"))
{
var featureImage = Umbraco.TypedMedia((int)item.GetPropertyValue("pictureOfTheProject"));
<img class="img-responsive" src="#featureImage.GetCropUrl(250, 250)" alt='#item.GetPropertyValue("titleOfTheProject")' />
<div class="overlay">
<div class="recent-work-inner">
<h3>#item.GetPropertyValue("titleOfTheProject")</h3>
<a class="preview" href="#featureImage.GetCropUrl(250, 250)" rel="prettyPhoto">
<i class="fa fa-eye"></i> View
</a>
</div>
</div>
}
#* Else when the project doesnt have a picture, show default one *#
else
{
var noImage = "http://www.don-zalmrol.be/media/1440/no_image_available.png";
<img class="img-responsive" src="#noImage.GetCropUrl(250, 250)" alt="No image" />
<div class="overlay">
<div class="recent-work-inner">
<h3>#item.GetPropertyValue("titleOfTheProject")</h3>
<a class="preview" href="#noImage.GetCropUrl(250, 250)" rel="prettyPhoto">
<i class="fa fa-eye"></i> View
</a>
</div>
</div>
}
</div>
</div>
}
}
#* Put the other tags under there own tab *#
else
{
}
}
#* End dynamic tags *#
</div>
</div>
}
#* No content matching the tag? *#
else
{
<p>There isn't any content matching that tag.</p>
#Html.Partial("TagList")
}
}
#* Show the tag list with amount *#
else
{
#Html.Partial("TagList")
}
}
EDIT 27-03-2016
Ok so I now know that I need to play around with my tag query or use the IEnumerable. But I can't seem to find it out how I can do this without breaking the code...
#* Get all tags and order them by name *#
var tagList = Umbraco.TagQuery.GetAllContentTags().OrderBy(t => t.Text);
#* Get requested tag *#
string tag = Request.QueryString["tag"];
#* Show all content by requested tag *#
var publishedContent = Umbraco.TagQuery.GetContentByTag(tag);
Above are the pieces of code that list all the tags I have in a var, gets the name from the URL (i.e. Electronics) and one that then displays all content that matches said queried tag.
So in short I need to change the last part of the TagQuery to list all content that has a tag and then filter it out by the querystring to display them in their own category.
But how can list all tagcontent?
Cheers,
Don
Your issue is on line 76 of the pastebin where you loop through the published content, which is already filtered by the selected tag. Because of this, only content with the selected tag ever gets written to the template.
What you need to do is use the loop on line 70 where you're looping through all tags. The jQuery plugin you're using will handle the filtering by using the CSS class you add on line 78. You can get rid of the loop on line 76.
For clarity, I would also change the
#foreach (var tags in tagList)
to
#foreach (var item in tagList)
since the foreach is producing a single tag rather than plural "tags" as your code insinuates. This has the added bonus of allowing you to keep the rest of your code the same once you remove the loop on line 76.

How to increment a variable in template without loop -- Play 2.1.1, Java

I would like to generate ID's for an HTML list.
The list is generated dynamically from the database.
I cant use a for loop or the list.zipWithIndex function because my logic contains a few loops for the generation of the list already, in which the counter needs to be incremented too. I also tried it with the defining function, but its not allowed to reasign values like this: #{id = id + 1}
Whats the best way to accomplish the generation of Id's?
Thats part of the template (uniqueId needs to be replaced with an integer):
<div id="tree">
<ul>
<li id="uniqueId">
<a class="dashboard" href="/">Dashboard</a>
</li>
<li id="uniqueId">
<b>Products</b>
<ul id="uniqueId">
#for(cat <- Application.allCategories()) {
<li id="uniqueId">
<a class="name" href="#routes.Categories.getd(cat.id).url">#cat.name</a>
<ul>
#for(prod <- Application.allProducts()) {
<li id="uniqueId">
<a class="name" href="#routes.Product.getById(prod.id).url">#prod.name</a>
</li>
#*more code and the closing tags...*#
Use just ... object's id prefixed to make it unique, example for first listing:
#for(cat <- Application.allCategories()) {
<li id="cat_#cat.id">
for second:
#for(prod <- Application.allProducts()) {
<li id="prod_#prod.id">
or if the same product can be displayed in several categories prefix it with cat.id as well:
#for(cat <- Application.allCategories()) {
<li id="cat_#cat.id">
#for(prod <- Application.allProducts()) {
<li id="prod_#(cat.id)_#(prod.id)">

On click event update template

I´m working on a meteor example. I get the value of one tag on click event on the link. That value is the same that is present on one collection inside doc "pet" or "zoo". I want to use this value to filter the content present on the template.
A minimal example:
{{#each Animal}}
<div>
<span> {{pet}} </span>
</div>
<div>
<span> {{zoo}} </span>
</div>
{{/each}}
After click:
{{#each Animal}}
<div>
<span> {{zoo}} </span>
</div>
{{/each}}
On this case when I get the value present in "zoo" I just want to update the template with all the the spans that contains elements on doc zoo, and that all related to pet dissappear.
The query to mongodb is working perfectly, my problem is that I´m a little bit confused.
Should I use helpers?
Thank you so much.
Let's see if I understood correctly your problem.
You should use a Session variable where you store the action you are doing. Then add a template if and print inside of this tag whatever you want to show at that time.
Let's do a minimal example:
<template name="showAnimalsTemplate">
{{if showAnimals}}
{{#each Animal}}
<div>
<span> {{pet}} </span>
</div>
<div>
<span> {{zoo}} </span>
</div>
{{/each}}
{{/if}}
{{if showZoo}}
{{#each Animal}}
<div>
<span> {{zoo}} </span>
</div>
{{/each}}
{{/if}}
Following this example, you add in the client javascript something like this:
Template.showAnimalsTemplate.showAnimals = function(){
if( Session.get('action') == 'showingTheZoo')
return true;
return false;
}
Template.showAnimalsTemplate.showZoo = function(){
if( Session.get('action') == 'showingTheZoo')
return true;
return false;
}
Don't forget to set the session value inside the click event.
Session.set('action', 'showingTheZoo');

jQuery: Convert to a single tag

I want to convert the below HTML to single tag:
Before :
<div class="Parent1">
<div class="Child1">
Child1
</div>
<div class="Child2">
Child2
</div>
</div>
<div class="Parent2">
<div class="Child1">
Child1
</div>
<div class="Child2">
Child2
</div>
</div>
After :
<div class="Parent1">
<button>Child1</button>
<button>Child2</button>
</div>
<div class="Parent2">
<button>Child1</button>
<button>Child2</button>
</div>
i tried with wrapping/unwrapping. But it does not work. This I want to be generic.
Try this (demo):
$('div[class^="Parent"]').each(function(index, parent) {
var html = [];
$('a', parent).each(function(index, child) {
html.push('<button>'+$(child).html()+'</button>');
});
$(parent).html(html.join(''));
});
It assumes that there is a pattern to the parent divs but that that isn't some common class name.
One way to do it:
$("a").unwrap().wrap("<button>").parent().text(function () {
return $(this).text();
});
http://jsfiddle.net/Tomalak/Gct7H/
Another one in the same spirit:
$("a").unwrap().replaceWith(function () {
return $("<button>", {text: $(this).text()});
});
PS: In reality you would want to select something more specific than just $("a"). Maybe $("#container a") or $("a.someClass"), depending on your markup.