Ember: Dynamically generate link-to html - javascript

I have a standard way in my ember app to display alert messages. I just pass it a string.
But now I want to include an ember link inside my message.
This is not working:
afterModel: function(model) {
if (model.length) {
this.send("addAlert", "Click {{#link-to 'reviews.show' 1}}here{{/link-to}} to see the review");
}
}
This is how the html is rendered:
{{#each alerts as |alert|}}
<div data-alert class="alert-box {{alert.state}} radius">
{{{alert.message}}}
<a class="close" {{action 'removeAlert' alert}}>x </a>
</div>
{{/each}}
With a handlebars tripple-thing {{{}}}
Any ideas on how to make this work?

Related

Meteor, How do I change a template (view) on event?

I'm building an app with two views: home & view list
When the user clicks on a list's name on the home view, it should change to the "view list" template. I've added a session variable called 'view', setting it to 'home' on startup. When a click event is detected on one of the items (list name) on the home screen, it changes the value of view to 'viewList'. Then in the HTML, I have an if statement to show the home template if 'view' is 'home', otherwise show the 'viewList' template.
I can tell the first part works because I'm outputting the value of 'view' and it does output the value "viewList" when you click on a list name, it just doesn't change the template.
What am I missing?
My code:
mylists.js:
Lists = new Mongo.Collection("lists");
if (Meteor.isClient) {
Meteor.startup( function() {
Session.set("view", "home");
});
Template.main.helpers({
view: function () {
return Session.get("view");
}
});
Template.home.helpers({
lists: function () {
return Lists.find({}, {sort: {lastUsed: -1}});
}
});
Template.home.events({
"submit #new-list": function (event) {
var name = event.target.listName.value;
Lists.insert ({
name: name,
createdAt: new Date(),
lastUsed: new Date()
});
},
"click .list-row": function (event) {
Session.set("view", "viewList");
}
});
}
mylists.html:
<head>
<title>My Lists</title>
</head>
<body>
{{> main}}
</body>
<template name="main">
{{view}}
{{#if view "home"}}
{{> home}}
{{else}}
{{> viewList}}
{{/if}}
</template>
<template name="home">
<header>
<h2>My Lists</h2>
</header>
<ul id="lists">
<li>
<form id="new-list">
<input type="text" name="listName" value="My List 1">
<input type="submit" value="Save">
</form>
</li>
{{#each lists}}
{{> list}}
{{/each}}
</ul>
</template>
<template name="viewList">
<header>
<h2>View List</h2>
</header>
<!-- list details will show here -->
</template>
<template name="list">
<li class="list-row" id="{{_id}}">{{name}}</li>
</template>
If you want to change from templates view i suggest you tu install the iron:router package.
run
meteor add iron:router
lester create the routes.js on the /lib folder
now lets do this step by step.
First create 1 template on myAppName/client/views/layout.html
<template name="layout">
{{> yield}}
</template>
and update the routes.js with this code.
Router.configure({
layoutTemplate: 'layout' // here we say that layout template will be our main layout
});
Now on the same routes.js create this 2 routes.
Router.route('/home', function () {
this.render('home');
});
Router.route('/viewList', function () {
this.render('viewList');
});
With this if you navigate to localhost:3000/home or /viewList you will see the html content on there.
NOTES: <header> inside the templates you don't need it.
Now this is just an example because i don't really know whats your main idea here.
you are calling {{#each lists}} inside the home template, so whats the point of having the viewList template?
Now if you want to create individual and dynamic routes for each list you can try this.
Router.map(function () {
this.route('listViews', {
path: '/listViews/:_id',
waitOn: function(){
return Meteor.subscribe('lists')
},
data: function(){
return Lists.findOne({_id: this.params._id});
}
});
});
Now with this, you have dynamic templates for each object on the List collection, if you go to localhost:300/listView/35dwq358ew for example you will get render the listView with some data.
you can do stuff like this inside the template list
<template name="list">
<li class="list-row" id="{{_id}}">{{name}}</li>
<li>Check this List in detail</li>
</template>
and the viewList template will look like this.
<template name="viewList">
<h2>{{title}}</h2>
<!-- list details will show here -->
{{informationAboutList}}
</template>

loading meteor template on click events

So, Basically i'm new to meteor(0.8.2) and trying to create a basic app having two templates(addnewPlace and Map) and a single button. What i need to get is that, when i click on "Add new Place" button, template "addNewPlace" should be loaded in body or else template "Map" should be loaded. Help will be appreciated :)
My html code:
<body>
{{> menu}}
{{> body}}
</body>
<template name="body">
{{#if isTrue}}
{{> addnewPlace}}// tested this template individually, it works.
{{else}}
{{> maps}} // tested this template individually, it works too.
{{/if}}
</template>
<template name="menu">
<h1>Bank Innovation Map</h1>
<input type="button" value="Add new Place">
</template>
My js code:
Template.body.isTrue = true;
Template.menu.events({
'click input': function(){
//load a new template
console.log("You pressed the addNewplace button");//this fn is called properly
Template.body.isTrue = true;
}
});
Well first of all you obviously aren't changing anything in the click event (true before, true after). But also if you did, I think you might be better off using a session variable for this, to maintain reactivity.
Session.setDefault('showAddNewPlace', false)
Template.body.isTrue = function() { Session.get('showAddNewPlace'); }
Template.menu.events({
'click input': function(){
//load a new template
console.log("You pressed the addNewplace button");//this fn is called properly
Session.set('showAddNewPlace', true)
}
});
Meteor 0.8.2 comes in with the dynamic template include feature. Just set a session variable value on click event and you would like to use the template name on the event.
Session.setDefault('myTemplate', 'DefaultTemplateName');
"click input": function (event) {
Session.set("myTemplate", 'template_name');
}
You can now write this:
<body>
{{> menu}}
{{> body}}
</body>
<template name="body">
{{> UI.dynamic template=myTemplate}}
</template>
<template name="menu">
<h1>Bank Innovation Map</h1>
<input type="button" value="Add new Place">
</template>
You may like to take a look at this article for the reference:
https://www.discovermeteor.com/blog/blaze-dynamic-template-includes/

Ember navbar UI condition based on currentPath

I must not be doing something right. I have the following:
application.hbs
{{#view App.NavbarView}}{{/view}}
{{outlet}}
with the following template for Navbar
_navbar.hbs
<div class="toolbar">
<div class="row">
<div class="absolute top-left">
<button {{action "back"}} class="btn passive back"><i class="fa fa-play"></i></button>
</div>
{{#if hasTabs}}
<div class="small-centered columns">
<div class="tabs">
<ul>
{{#link-to 'stories' tagName="li" class="tab"}}<i class="fa fa-book"></i> Stories{{/link-to}}
{{#link-to 'mylevels' tagName="li" class="tab"}}<i class="fa fa-user"></i> My Levels{{/link-to}}
{{#link-to 'arcade.index' tagName="li" class="tab"}}<i class="fa fa-gamepad"></i> Arcade{{/link-to}}
</ul>
</div>
</div>
{{else}}
<div class="small-6 small-offset-3 columns">
<h2 class="title">{{ pageTitle App.currentPath }}</h2>
</div>
{{/if}}
{{#if currentUser.userName}}
<div class="absolute top-right">
<span class="user-hello">Welcome Back, <strong>{{ currentUser.userName }}</strong></span>
<button {{action "transitionAccount" currentUser._id}} class="square logged-in"><i class="fa fa-user"></i></button>
</div>
{{ else }}
<div class="absolute top-right">
<button {{action "transitionLogin"}} class="square logged-out"><i class="fa fa-user"></i></button>
</div>
{{/if}}
</div>
</div>
So all it is is a typical fixed navbar and in the middle of it I display what page you are on, if you happen to be on a page that has tabbed content, I show a tab container instead.
So I'm just using this.get('currentPath') in my App controller and comparing it against a group of route names to trigger true/false (I need an observer so it looks at the route change since the Navbar is in inline view at the Application level).
app.js
App.ApplicationController = Ember.ObjectController.extend({
updateCurrentPath: function() {
App.set('currentPath', this.get('currentPath'));
}.observes('currentPath'),
tabs: function() {
var route = this.get('currentPath'),
group = ['arcade.index', 'mylevels', 'stories', 'arcade', 'arcade.loading'];
console.log("ROUTE: ", route);
var tabs = group.indexOf(route) > -1 ? true : false;
return tabs;
}.observes('currentPath'),
// no idea what to do here
hasTabs: function() {
this.tabs();
}.property('tabs')
});
So, basically, no matter what, the tab UI is showing up, but I only want it to show up if that tabs observer is true. With some debugging I'm getting all the console output I would expect but I tried just doing {{#if tabs}} (just using the observer directly) and that always fires true (always shows the tabs UI). I assumed that's because it was an observer and not an actual controller property I could use in my template, so I tried just setting the hasTabs property and referencing the observer, but that doesn't seem to work. I realize I am fundamentally not understanding how this should work. Any thoughts?
If I understand your question correctly you should be able to just change your code to this (renamed tabs to hasTabs, removed previous hasTabs function. Changed from observes currentPath to be property of current path, removed the tabs variable assignment and replaced with the return, reduced the boolean conditional to the simple comparison operator). This is what I'd do, anyway. :) H2H
hasTabs: function() {
var route = this.get('currentPath'),
group = ['arcade.index', 'mylevels', 'stories', 'arcade', 'arcade.loading'];
return group.indexOf(route) > -1;
}.property('currentPath')

Why can't I change this emberjs model without an error?

I'm writing an ember app that has this settings model that I'm using to bypass routing because I want a single url. However, I get this error:
Error: Cannot perform operations on a Metamorph that is not in the DOM.
when I change any of the data in the model. I'm relatively new to ember, but from what I read of what's out there, you should be able to change a model just fine. Here's my html file:
<script type="text/x-handlebars" data-template-name="index">
<div class="navbar navbar-fixed-top">
<div class="navbar-inner">
<div class="container">
<a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</a>
<a class="brand" href="#"><img src="./img/MySpending.png"></a>
<div class="nav-collapse">
<ul id="navWrapper" class="nav">
{{#if model.isIndex}}
{{partial "indexNav"}}
{{/if}}
{{#if model.isMonthly}}
{{partial "monthlyNav"}}
{{/if}}
{{#if model.isYearly}}
{{partial "yearlyNav"}}
{{/if}}
</ul>
</div>
<div class='navbar_form pull-right'></div>
</div>
</div>
</div>
<div id="bodyWrapper" class="container" style="padding-top:60px;">
{{#if model.isIndex}}
{{partial "indexPage"}}
{{/if}}
{{#if model.isMonthly}}
{{partial "monthlyPage"}}
{{/if}}
{{#if model.isYearly}}
{{partial "yearlyPage"}}
{{/if}}
</div>
{{outlet}}
And each partial works fine initially.
It's when I change it in the three regular functions in my app.js that I have a problem. Here's my app.js:
App = Ember.Application.create();
App.Router.map(function() {
// put your routes here
});
App.settings=Ember.Object.extend({
isIndex: true,
isMonthly: false,
isYearly: false
});
Settings=App.settings.create();
//App.SettingsController = Ember.ObjectController.extend(Settings); Not sure whether to put this in or not
App.IndexRoute = Ember.Route.extend({
model: function() {
return Settings;
}
});
function goToMonthly(){
Settings.set('isIndex',false);
Settings.set('isMonthly',true);
Settings.set('isYearly',false);
}
function goToYearly(){
Settings.set('isIndex',false);
Settings.set('isMonthly',false);
Settings.set('isYearly',true);
}
function goToIndex(){
Settings.set('isIndex',true);
Settings.set('isMonthly',false);
Settings.set('isYearly',false);
}
So I'm not sure where the error is but if anyone can help,it'd be extremely appreciated.
There's a fundamental error in your approach here. Your Route/Controller are meant to handle the model for you, so by declaring your model outside of these objects, you're making more work for yourself and leading yourself to potential trouble.
I'm not seeing where your "goTo" functions are being called (I assume they're inside the partials, but if they're in that HTML and I missed it, that's my bad). But here's how they should be referenced:
App.SettingsController = Ember.ObjectController.extend({
actions:{
goToMonthly:function(){
this.set('isIndex', false);
this.set('isMonthly', true);
this.set('isYearly', false);
},
//Other functions go here
}
});
Then within your html, call these actions using handlebars:
<a {{action 'goToMonthly'}}> Click this to run the action </a>
Which ember version are you using? I tried it with ember 1.2 and 1.3 and your example is working fine.

emberjs append works but raises Assertion Failed error

I'm new to ember I am trying to append a template to another and it seems to work but it raises an error, can you please explain why?
The error:
Assertion failed: You cannot append to an existing Ember.View. Consider using Ember.ContainerView instead
This is the code in app.js
App.NewStickie = Ember.View.extend({
click: function(evt){
var stickie = Ember.View.create({
templateName: 'stickie',
content: 'write your notes here'
});
stickie.appendTo('#stickies');
}
});
These are the contents of index.html
<script type="text/x-handlebars">
{{#view App.NewStickie}}
<button type="button" class="btn btn-success">
New
</button>
{{/view}}
{{outlet}}
</script>
<script type="text/x-handlebars" id="index">
<div id="stickies">
{{#each item in model}}
<div class="stickie" contenteditable="true">
{{#view App.DeleteStickie}}
<span class="glyphicon glyphicon-trash"></span>
{{/view}}
<div contenteditable="true">
{{item.content}}
</div>
</div>
{{/each}}
</div>
</script>
<script type="text/x-handlebars" data-template-name="stickie">
<div class="stickie">
{{#view App.DeleteStickie}}
<span class="glyphicon glyphicon-trash"></span>
{{/view}}
<div contenteditable="true">
{{view.content}}
</div>
</div>
</script>
Each view in ember have a template, for example:
foo_view.js
App.FooView = Ember.View.extend({
templateName: 'foo'
})
foo template
<script type="text/x-handlebars" data-template-name="index">
<div id=myFooView>Foo</div>
</script>
You are trying to insert a view inside of other in that way:
App.BarView.create().appendTo('#myFooView')
This isn't allowed. You can use the {{view}} handlebars helper to render a view inside other like that:
foo template
<script type="text/x-handlebars" data-template-name="index">
<div id=myFooView>
Foo
{{view App.BarView}}
</div>
</script>
But I think that you want this working dynamically. So you can use the ContainerView, like described by the error message:
App.StickiesView = Ember.ContainerView.extend({
click: function() {
var stickie = Ember.View.create({
templateName: 'stickie',
content: 'write your notes here'
});
this.pushObject(stickie);
}
})
I see in your code a lot of views with the click event, ember encourage you to use actions, this give more flexibility, error/loading handling etc. I think is a good idea to use it.
I hope it helps
You should probably read this guide that explains that ContainerView is. Also, I don't think it's necessary to create another View to append a template to another template.

Categories

Resources