EmberJS passing variable to the layout - javascript

I have a really basic test app. It has 2 pages an empty index and a login form on an other page. And I would like to know how can I pass variables to the layout which is the same on every page (user name/status for display it and display login/logout by status).
Or the best solution could be something like this: pass a user model to the AppController which is available from anywhere (like login page or login modals) and If I change and save the data the site template is just changing by the passed variables.
I was trying to find a good way, I find a few ones but none of them was working :( If you know a working way please share me or show me what is the solution.
Here is my code: the JS I have:
App = Ember.Application.create();
App.ApplicationController = Ember.Controller.extend({});
/* *** Index *** */
App.IndexView = Ember.View.extend({
layoutName: 'app_view'
});
App.IndexController = Ember.Controller.extend({
});
/* *** LOGIN *** */
App.Router.map(function () {
this.resource("login");
});
App.LoginView = Ember.View.extend({
layoutName: 'app_view'
});
App.LoginController = Ember.Controller.extend({
login: function() {
// blah blah
}
});
Layout template:
<script type="text/x-handlebars" data-template-name="app_view">
<header id="site">
{{#link-to "index"}}Index{{/link-to}}
{{#link-to "login"}}Login{{/link-to}}
{{variablaWhatIWant}}
</header>
<hr />
<section id="content">
{{yield}}
</section>
<hr />
<footer id="main">
Footer
</footer>
</script>
2 view templates:
<script type="text/x-handlebars" data-template-name="index">
Hello INDEX page!
</script>
<script type="text/x-handlebars" data-template-name="login">
<form class="form-horizontal" {{action "login" on="submit"}}>
<div class="control-group">
Username: {{input value=username type="text"}}
</div>
<div class="control-group">
Password {{input value=password type="password"}}
</div>
<button type="submit" class="btn">Log in!</button>
</form>
</script>
Thank you very much! :)

You have the right idea, see managing dependancies between controllers http://emberjs.com/guides/controllers/dependencies-between-controllers
just set the needs property, and you can access that controller from any other
App.LoginController = Ember.Controller.extend({
needs: ['application'],
login: function() {
this.authenticate(this.get('username'), this.get('password')).then(function(user) {
this.set('controllers.application.user', user);
},
function(err) {
//auth err
}
}
});

Related

Getting proper data model when rendering into outlet in Ember

I am trying to create a select box in a modal that has a list of all cars in inventory. If I enter the app from a page that has all the data loaded and open the modal it works correctly. However if I am on a route that doesn't have the data loaded, then open the modal the select options do not show. Furthermore the options will never update from that point. How do I get the proper data to load? Should not this work since I am fetching the data direct from the store?
Dms.SellDialogController = Ember.ArrayController.extend({
cars: function() {
return this.store.find('car').filterProperty('isSold', false);
}.property('model'),
selectedCar: '',
carObjects: function() {
var cars = this.get('cars').map(function(car) {
return obj = {id: car.get('id'), key: car.get('keyNumber'), label: 'KEY#:'+car.get('keyNumber') + ' - STOCK#:' + car.get('stock')+' '+car.get('year')+' '+car.get('vModel')};
});
cars.sort(function(a, b){return a.key-b.key;})
return cars;
}.property('route'),
title: 'Select the car you are selling'
});
ApplicationRoute...
...
openModal: function(modalName, model) {
this.render(modalName, {
into: 'application',
outlet: 'modal',
model: model
});
},
Action to open modal inside application template
{{action 'openModal' 'sellDialog' model}}
and the templates
<script type="text/x-handlebars" id="components/modal-dialog">
<div class='modal-overlay' {{action "closeModal" target=cntrllr}}>
<div class='modal' {{action "dngn" target=cntrllr bubbles=false}}>
<div class='modal-content'>
<div class='modal-header'>
<button type="button" class="close" aria-label="Close" {{action "closeModal" target=cntrllr}}><span aria-hidden="true">×</span></button>
<h4 class="modal-title">{{title}}</h4>
</div>
{{yield}}
</div>
</div>
</div>
</script>
<script type="text/x-handlebars" data-template-name="sellDialog">
{{#modal-dialog title=title cntrllr=controller model=model}}
<div class="modal-body">
<span class='pull-left'>Key Number:
{{view "select" content=carObjects optionValuePath="content.id" optionLabelPath="content.label" selection=selectedCar}}
</span>
</div>
<div class="modal-footer">
<button {{action "modalAction" controller "sell" selectedCar.id}}>Sell</button>
<button {{action "closeModal"}}>Cancel</button>
</div>
{{/modal-dialog}}
</script>
UPDATE 2015/5/4:
I have a hack to make it work for now. I added a cars property in the application route and I am getting it in SellDialogController. This looks like a bug to me. I will make a jsFiddle when I get some time and check it further before issuing a bug report.
try using setupController on the Application Route, since setupController is always called.
import Ember from 'ember';
export default Ember.Route.extend({
model: Ember.RSVP.hash({
optionsForSelect: function() {
// fetch your select data here
},
otherData: function() {
// get your regular model data here
}
}),
setupController: function(controller, model) {
this.controllerFor('sellDialog').set('model', model.optionsForSelect);
this.controller.set('model', model.otherData);
}
});

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>

Output in multiple parts of the page

index.html:
<div id="section_a">
<script type="text/x-handlebars" data-template-name="index">
First value: {{input type="text" value=one}}<br>
Second value: {{input type="text" value=two}}<br>
Result: {{result}}
</script>
</div>
<div id="section_b">
<!-- Display {{result}} here aswell -->
</div>
application.js
App.IndexController = Ember.ObjectController.extend({
one: 0,
two: 0,
result: function() {
return this.get('one') + this.get('two');
}.property('one', 'two')
});
I have a section of a page where I have some Ember interactions - a user inputs some values and a calculated result is displayed.
I now want to have the calculated result displayed in another part of the page that is outside the defined template, how would I accomplish this?
You could bind that property to the application controller and then use it anywhere you want in the application. For example:
App.ApplicationController = Ember.ObjectController.extend({
needs: ['index'],
indexResult: Ember.computed.alias('controllers.index.result')
});
Now you can use it anywhere within the application you want. Just tell the outlet's controller to need the application controller, then call that property in the template. Like so:
App.FooController = Ember.ArrayController.extend({
needs: ['application'],
//...
});
In the Foo template:
{{controllers.application.indexResult}}
Or, if you are within the application scope you can just do:
{{indexResult}}
One simple and straight forward solution would be:
<script type="text/x-handlebars" data-template-name="index">
<div id="section_a">
First value: {{input type="text" value=one}}<br>
Second value: {{input type="text" value=two}}<br>
Result: {{result}}
</div>
<div id="section_b">
<!-- Display {{result}} here aswell -->
</div>
</script>
You can also set up another outlet that can be used to display the "result". You can read more about using outlets in the Ember.js Guides at http://emberjs.com/guides/routing/rendering-a-template/

Render view component with parameters into named outlet ember.js

I have 2 named outlets in my application template, slider-area and pre-footer. Is there a way to pass view components that take parameters, as in the main-slider component shown in the index template, to a named outlet? So I would need to pass {{main-slider sliders=filteredSlider}} to the outlet {{outlet "slider-area"}} in the index template?
I have come from rails so forgive my thinking if this is not how ember does it. One could specify yield :slider_area in the application template and then wrap any content for this area in a content_for :slider_area block. Is a similar approach available in ember?
index.html
<script type="text/x-handlebars" data-template-name="application">
{{panasonic-topbar}}
<div id="batterywrap">
{{outlet "slider-area"}}
<div class="index_contents">
<div class="index_contents_inner">
<div class="main_area">
{{outlet}}
</div>
</div>
</div>
</div>
{{panasonic-footer}}
</script>
<script type="text/x-handlebars" data-template-name="index">
# Something like {{outlet "slider-area" render main-slider sliders="filteredSlider}} ?
{{main-slider sliders=filteredSlider}}
{{partial "social_footer"}}
</script>
app.js
App.IndexController = Ember.ObjectController.extend({
filteredSlider: function(){
return this.get('sliders').filterBy('page', 'index');
}.property('sliders.#each.page')
});
App.IndexRoute = Ember.Route.extend({
model: function() {
return Ember.RSVP.hash({
sliders: this.store.find("slider")
});
}
});
Ok, so I have a solution to this issue, rather than trying to pass a view component within a template to a specific outlet (the ruby on rails way), the key is to create a template, not a component, and render this from within the Route to the specific outlet.
When rendered from Route, the slider template has access to all of the functions in the scope of the Routes controller, so we namespace the functions/arguments universally across all controllers that will use this template and our dynamic parameters should work.
So below in the IndexRoute we define the data that we send to the controller, sliders, we also specify that we want to render normal content in the default outlet using this.render();, and then we render our shared slider template into the named outlet "slider-area". Then in our controller, we filter the models data to our specification and we name this function batterySliders across all controllers that use this feature.
app.js
App.IndexController = Ember.ObjectController.extend({
batterySliders: function(){
return this.get('sliders').filterBy('page', 'index');
}.property('sliders.#each.page')
});
App.IndexRoute = Ember.Route.extend({
model: function() {
return Ember.RSVP.hash({
sliders: this.store.find("slider"),
});
},
renderTemplate: function(){
this.render();
this.render("slider", {outlet: "slider-area"});
}
});
index.html
<script type="text/x-handlebars" data-template-name="slider">
{{panasonic-navigation tagName="div" classNames="gnavi_area"}}
<div class="slider_wrap">
<div id="slider" class="main_slider">
{{#each slider in batterySliders}}
<div class="slider_area slider0{{unbound slider.id}} {{unbound slider.background}}">
<div class="slider_inner">
<div class="inner0{{unbound slider.id}}">
<img {{bind-attr src="slider.image" alt="slider.image"}} class="nosp"/>
<img {{bind-attr src="slider.sm_image" alt="slider.sm_image"}} class="sp"/>
</div>
</div>
</div>
{{/each}}
</div>
</div>
</script>
application.html
<script type="text/x-handlebars" data-template-name="application">
{{panasonic-topbar}}
<div id="batterywrap">
<div class="content_head">
{{outlet "slider-area"}}
</div>
<div class="index_contents">
<div class="index_contents_inner">
<div class="main_area">
{{outlet}}
</div>
</div>
</div>
</div>
{{panasonic-footer}}
</script>

Ember -retrieving data from server to a view?

Does anyone happen to know How to show data from server, right now ive been showing related models in basic handlebars like this
{{
view Ember.Select
prompt="Organization"
contentBinding="organizations"
optionValuePath="content.id"
optionLabelPath="content.name"
selectionBinding="selectedOrganization"
}}
But i need to create an has many form... which im duplicating using views? Is using views even the right path to go ?!
{{#each view.anotherField}}
{{view Ember.TextField value=view.name}}
{{/each}}
Here is the output of my form, u can see Organizatons form being doubled
JSbin http://jsbin.com/efeReDer/7/edit
Today I came up with this... :D Kinda serves the purpose ? looks ugly tho
http://emberjs.jsbin.com/acUCocu/6/edit
Basically i made an empty model which i then each loop.
On action i "store.create".empty record to it.
Give me your thoughts on this :)
Also is there a way to make these fields indepedent ? without all changing their content while an input is changed.
Cheers,
kristjan
Here you can find an example to work on, of what i think you are asking
http://emberjs.jsbin.com/iPeHuNA/1/edit
js
Tried to separate the entities related to the model of the app, from how they will be displayed.Created an ember class App.Person that will hold the data from server. I have not used ember-data, but it is quite easy to replace the classes with ember-data notation and the dummy ajax calls with respective store calls etc, if desired.
App = Ember.Application.create();
App.Router.map(function() {
this.route("persons");
});
App.IndexRoute = Ember.Route.extend({
beforeModel: function() {
this.transitionTo("persons");
}
});
App.PersonsRoute = Ember.Route.extend({
model:function(){
return $.ajax({url:"/"}).then(function(){/*in url it is required to place the actual server address that will return data e.g. from a rest web service*/
/*let's imagine that the following data has been returned from the server*/
/*i.e. two Person entities have already been stored to the server and now are retrieved to display*/
var personsData = [];
var person1 = App.Person.create({id:1,fname:"Person1",lname:"First",genderId:2});
var person2 = App.Person.create({id:2,fname:"Person2",lname:"Second",genderId:1});
personsData.pushObject(person1);
personsData.pushObject(person2);
return personsData;
});
},
setupController:function(controller,model){
/*this could also be retrieved from server*/
/*let's mimic a call*/
$.ajax({url:"/",success:function(){
/*This will run async but ember's binding will preper everything.If this is not acceptable, then initialization of lists' values/dictionary values can take place in any earlier phase of the app. */
var gendersData = [];
gendersData.pushObject(App.Gender.create({id:1,type:"male"}));
gendersData.pushObject(App.Gender.create({id:2,type:"female"}));
controller.set("genders",gendersData);
model.forEach(function(person){
person.set("gender",gendersData.findBy("id",person.get("genderId")));
});
}});
controller.set("model",model);
}
});
App.PersonsController = Ember.ArrayController.extend({
genders:[],
actions:{
addPerson:function(){
this.get("model").pushObject(App.Person.create({id:Date.now(),fname:"",lname:""}));
},
print:function(){
console.log(this.get("model"));
}
}
});
App.PersonFormView = Ember.View.extend({
templateName:"personForm",
/*layoutName:"simple-row"*/
layoutName:"collapsible-row"
});
App.Person = Ember.Object.extend({
id:null,
fname:"",
lname:"",
gender:null
});
App.Gender = Ember.Object.extend({
id:null,
type:null
});
html/hbs
created a view that takes care of how each App.Person instance gets rendered. As example partial and layouts have been used to accomodate bootstrap styling, as i noticed you used some in your example.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Ember Starter Kit</title>
<link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/normalize/2.1.0/normalize.css">
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.css">
</head>
<body>
<script type="text/x-handlebars">
<h2>Welcome to Ember.js</h2>
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="persons">
{{#each person in this}}
{{view App.PersonFormView}}
{{/each}}
<br/><br/>
{{partial "buttons"}}
</script>
<script type="text/x-handlebars" data-template-name="_buttons">
<button type="button" class="btn btn-primary" {{action "addPerson"}}>
add
</button>
<button type="button" class="btn btn-primary" {{action "print"}}>
print results to console
</button>
</script>
<script type="text/x-handlebars" data-template-name="personForm">
<div class="row">
<div class="col-md-6 col-xs-5">
<div class="form-group">
<label>First Name</label>
{{input class="form-control" placeholder="First Name" value=person.fname}}
</div>
</div>
<div class="col-md-6 col-xs-5">
<div class="form-group">
<label>Last Name</label>
{{input class="form-control" placeholder="Last Name" value=person.lname}}
</div>
</div>
</div>
<div class="row">
<div class="col-md-2 col-xs-4">
{{
view Ember.Select
prompt="Gender"
content=controller.genders
optionValuePath="content.id"
optionLabelPath="content.type"
selectionBinding=person.gender
class="form-control"
}}
</div>
</div>
<!--</div>-->
</script>
<script type="text/x-handlebars" data-template-name="simple-row">
<div class="row">
{{yield}}
</div>
<br/><br/>
</script>
<script type="text/x-handlebars" data-template-name="collapsible-row">
<div class="panel-group" >
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title">
<a data-toggle="collapse" href=#{{unbound person.id}}>
person:{{person.fname}}
</a>
</h4>
</div>
<div id={{unbound person.id}} class="panel-collapse collapse">
<div class="panel-body">
{{yield}}
</div>
</div>
</div>
</div>
</br>
</script>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script src="//netdna.bootstrapcdn.com/bootstrap/3.0.3/js/bootstrap.min.js"></script>
<script src="http://builds.handlebarsjs.com.s3.amazonaws.com/handlebars-v1.1.2.js"></script>
<script src="http://builds.emberjs.com/tags/v1.2.0/ember.min.js"></script>
</body>
</html>

Categories

Resources