I have an ember.js model and controller setup like so:
//model
App.Order = Ember.Object.extend({
content: null,
create: function(data) {
this.set('content', data);
return this._super();
}
});
//orders controller
App.ordersController = Ember.ArrayController.create({
content: [],
init: function() {
var self = this;
var orders = [];
$.getJSON('js/data.json', function(data) {
data.forEach(function(item) {
var order = App.Order.create(item);
orders.push(order);
});
self.set('content', orders);
});
},
selectItem: function(data) {
alert(data);
}
});
With the following view:
{{#each App.ordersController}}
<div {{action selectItem target="App.ordersController"}}>{{order_number}}</div>
{{/each}}
Which prints out a list of orders with a click action that alerts the corresponding item. This works fine.
What I want to do is show a clicked item in a separate view, eventually with the goal of creating a floating dialogue with orders details shown. I'm new to ember and not sure how this should be implemented. I have a function selectItem which alert's a clicked order but I need to link this to a separate view and print the order details.
Should I store the selected item in a separate controller with a corresponding view and update this when selectItem is clicked? Or could I update a sperate view from the ordersController? Any advice is much appreciated.
When you use the router ember does the instantiation of your class for you. By specifing the "orders" route is looks for a template called orders and a controller called OrdersController if it can't find one it'll generate one for you. (I've omitted the controller for clearity). To load your model from a json source you could have a look at ember-data.
here is a jsfiddle so you can fiddle with it a bit.
You should definitely have a look here these are guides for ember that really help you on the way. The documentation is getting better and better. :)
JS:
window.App = App = Em.Application.create();
//model
App.Order = Ember.Object.extend({
order_number: null,
});
//we create 2 routes one for all the order and one per specific order
App.Router.map(function(){
this.resource('orders', { path: "/" });
this.resource("order", { path: "/:order_id" });
});
//the route supplies the model and handles the event to transition to a new route.
App.OrdersRoute = Em.Route.extend({
events: {
selectItem: function (orderId) {
//select the order by the "orderId" you want as a model for your new view.
this.transitionTo("order", order);
}
},
model: function(){
return content; //an array containing the orders;
}
});
//supplies the model for the "order" route by selecting one acording to the params;
App.OrderRoute = Em.Route.extend({
model: function(params){
return order; //select an object from the array according to the params
},
});
HBS:
<script type="text/x-handlebars" data-template-name="orders">
{{#each controller}}
<!-- this calls the event handler "selectItem" on the ordersroute -->
<div {{action "selectItem" order_number}}>{{order_number}}</div>
{{/each}}
<!-- this is handled by "App.OrderRoute" -->
<a href="#/3"/>with a direct link</a>
</script>
<script type="text/x-handlebars" data-template-name="order">
{{content.order_number}}
{{#linkTo "orders"}}Back to orders{{/linkTo}}
</script>
Related
I've been trying to figure this out for most of today and it's driving me insane, because I think i'm almost there, but just can't figure the last part out...
I have a route, called Map, which renders a sidebar, and within it has a named outlet for sidebar content:
map.hbs:
<div id="map-container">
{{render sidebar}}
<div id="map-canvas">
</div>
</div>
...
sidebar.hbs:
<div id="content-menu">
{{outlet sidebar-content}}
</div>
Each menu item in my sidebar has a custom action called loadModule, which performs a render of a named view into the sidebar-content outlet (using {{action 'loadModule' 'sidebar.module'}}):
var MapRoute = App.AuthenticatedRoute.extend({
actions: {
loadModule: function(module) {
//load a valid view template into the view
this.render(module,
{
into: 'sidebar',
outlet: 'sidebar-content'
});
}
}
});
module.exports = MapRoute;
Any action within the controller for that view works fine, I can trigger them from buttons etc, or by calling them in a didInsertElement in the SidebarModuleViews.
My issue is that I can't define a model for these views, so if I try and get data from my API in any of their Controllers, it won't render that data out to the templates.
I tried to use link-to, but I couldn't make the template append to the current viewport, rather than refreshing the entire page, which defeats the point of having a sidebar (I don't want the route to change)
var SidebarUserController = App.ApplicationController.extend({
actions: {
doSomething: function() {
alert('SOMETHING');
},
fetchUserProfile: function() {
//do something
var mod = this.store.find('profile', App.Session.get('uid'));
}
}
});
I can trigger either of those actions from the rendered template once it's rendered, however, although my store updates with the record, the handlebars helpers in the sidebar/user.hbs do not populate with the model data.
Here is my model:
var Profile = DS.Model.extend({
uid: DS.attr('string'),
firstName: DS.attr('string'),
lastName: DS.attr('string'),
gender: DS.attr('string'),
DOB: DS.attr('date'),
email: DS.attr('string')
});
module.exports = Profile;
and here is my sidebar/user.hbs:
<div class="container">
<button {{action 'doSomething'}}>Do A Thing</button>
<h1>{{firstName}} {{lastName}}</h1>
<h4>{{id}}</h4>
{{#if isAuthenticated}}
<a href="#" {{action 'logout'}}>Logout</a>
{{/if}}
</div>
In that template, the firstName, lastName and id fields do not populate, even though i'm pulling the data from the API and successfully storing it.
Additionally, if it helps, my router.map for sidebar/user looks like this:
this.resource('sidebar', function() {
this.route('user');
});
I believe that the fundamental issue here is that I can't work out how to set the model for the controller without triggering the route. Am I going about this wrong?
Ok so i've worked this out for my particular instance. It may not be the best way of doing it, but it's what I need:
In my MapRoute, I setup the model and controller for my additional sidebar menus in the setupController function. Doing this allows me to load critical data (such as user profile etc), on page load, and I can still retain the render function for each sidebar module in the Route, which will allow the intial data to load, and still allow me to update the model data for each sidebar module controller on subsequent functions:
map_route.js:
actions: {
loadModule: function(module) {
this.render(module, {into: 'sidebar', outlet: 'sidebar-content'});
}
},
setupController: function(controller, profile) {
var model = this.store.find('profile', App.Session.get('uid'));
var controller = this.controllerFor('sidebar.user');
controller.set('content', model);
},
...
I am trying to access backbone views in sails framework. I fetch data from the server and I am trying to push them to the DOM. Actually, I ve created a model-controller for the tags I store data from mongo to tags model and I return to backbone the url of tags view. I want to display those data to my DOM element. I am trying to find how is it possible to do so in the sails since I am receiving is not defined error. My DOM element code is the following:
51| <div id="profiles" class = 'hashTagsCloud'>
52| <script id="profileTemplate" type="text/template">
>> 53| <%= tagsCloud.tags.join("     ")%>
54| </script>
55| </div>
tagsCloud is not defined
Where tagsCloud is a item of the json file I got from the server. Backbone code for views:
var ProfileView = Backbone.View.extend({
el: "#profiles",
template: _.template($('#profileTemplate').html()),
render: function(eventName) {
_.each(this.model.models, function(profile){
var profileTemplate = this.template(profile.toJSON());
//push data to obj for map script
obj = profile.toJSON();
// Add data to DOM element
$(this.el).html(profileTemplate);
}, this);
return this;
}
});
The above backbone logic works like a charm in apache. However in sails I got not defined error. How can I define, in proper way, a view for tagsCloud item in my index file??
My json file is the following:
[
{
tstamp: 1366626103000,
tagsCloud: {
sort: "asc",
tags: [
"Lorem ipsum dolor sit amet consectetur"
]
},
id: "529da369380eb213e804a673"
}
]
Moreover I add some actions in my homeController file so as to send to ejs file the json data:
index: function (req,res)
{
console.log(req.tags); // tags is the name of the model-controller
res.view({
tags: req.tags
});
},
'home': function (req,res)
{
res.view();
}
Is there anything I ve to change in backbone view code to properly update my index view???
Finally I found a way to send fetched data to the DOM from the backbone. I ve changed template code inside backbone code, so as to send directly the data to the DOM element. Here is my code:
_.templateSettings = {
interpolate : /\{\{(.+?)\}\}/g
};
var TagsView = Backbone.View.extend({
el: "#profiles",
template: _.template("<div><p>{{ tg.tagsCloud.tags }}</p></div>"),
render: function(eventName) {
_.each(this.model.models, function(tags){
var tagsTemplate = this.template(tags.toJSON());
//push data to obj for map script
tg = tags.toJSON();
// Add data to DOM element
$(this.el).html(tagsTemplate);
}, this);
return this;
}
});
I want to implement a system that shows me the newest posts. For this I do not want to use the index action from the user as this is already taken for another post function but a "newest" action. It is showed on the index route with a {{ render "postNewest" }} call. I would prefer to load the data in the PostNewestController or PostNewestView instead of the route for abstraction reasons.
I tried two ideas to achieve this, but none worked so far:
create a custom adapter and add a findNewest() method: the findNewest() method is sadly not found when trying to call in the init method of the controller.
write the request directly into the init method and then update with store.loadMany(payload): data is successful request. However, I do not know how to access the data from the template and set the content of the controller.
Is there any way for this?
EDIT:
Here is the source code to better understand the problem:
PostModel.js
App.Post.reopenClass({
stream: function(items) {
var result = Ember.ArrayProxy.create({ content: [] });
var items = [];
$.getJSON("/api/v1/post/stream?auth_token=" + App.Auth.get("authToken"), function(payload) {
result.set('content', payload.posts);
});
return result;
}
});
PostStreamController.js
App.PostStreamController = Ember.ArrayController.extend({
init: function() {
this.set("content", App.Post.stream());
},
});
index.hbs
{{# if App.Auth.signedIn}}
{{ render "dashboard" }}
{{else}}
{{ render "GuestHeader" }}
{{/if}}
{{ render "postStream" }}
postStream.hbs
{{#each post in model}}
<li>{{#linkTo 'post.show' post data-toggle="tooltip"}}{{post.name}}{{/linkTo}}</li>
{{else}}
Nothing's there!
{{/each}}
PostShowRoute.js
App.PostShowRoute = Ember.Route.extend({
model: function(params) {
return App.Post.find(params.post_id);
},
setupController: function(controller, model) {
controller.set('content', model);
},
});
I Had this issue too. Just add init in your controller, and define the model you want to get there.
In your Controller
App.PostRecentController = Ember.ArrayController.extend({
init: function() {
return this.set('content', App.Post.find({
recent: true
}));
}
});
In your template
{{#each post in content}}
{{post.body}}
{{/each}}
I would recommend you check EMBER EXTENSION, it will give you a good idea of the naming, and see if everything is missing.
I figured out the problem. The Post model has a belongsTo relationship to another model. This relationship is not loaded by Ember so in the stream() method I have to load this manually. Then everything works as expected.
How do I pass data between two different routes and templates?
I have a javascript file on the front end (client folder) that simply calls Router.go() passing in the post ID as one of my parameters.
Below are the three main culprits (I believe). I've removed most of the code to make it easier to read. I can change to the PostDetail page with no problems. I can also retrieve the PostId on the PostDetail page from the Router. My problem is, the database entry (POLL) that is retrieved does not get rendered on the template. Hence {{Question}} is always blank even though the database entry is being returned.
Let me know if I should post more information.
FrontEnd.js
Template.PostTiles.events({
// When a choice is selected
'click .pin' : function(event, template) {
Router.go('Post', {_PostId: this.PostId});
}
});
post-detail.html
<template name="PostDetail">
<h3>{{Question}}</p>
</template>
Shared.js
Router.map( function() {
this.route('Home', {
path: '/',
template: 'PostTiles',
data: {
// Here we can return DB data instead of attaching
// a helper method to the Template object
QuestionsList: function() {
return POLL.find().fetch();
}
}
});
this.route('Post', {
template: 'PostDetail',
path: '/Post/:_PostId',
data: function() {
return POLL.findOne(this.params._PostId);
},
renderTemplates: {
'disqus': {to: 'comments'}
}
});
});
----- Update -----
I think I've narrowed down the issue to simply being able to render only one Database entry, instead of a list of them using the {{#each SomeList}} syntax.
Looks like you found the answer / resolved this, but just in case, I think it's in your findOne statement:
data: function() {
return POLL.findOne(this.params._PostId);
},
should read:
data: function() {
return POLL.findOne({_id:this.params._PostId});
},
(assuming that POLL has your posts listed by _id.
Hope that helps.
Could you pass the info in the Session? the docs for that are here http://docs.meteor.com/#session. That's what I'm planning on doing.
I have a parent view ProductListView containing multiple child views ProductView in a multi-step wizard. When a user click on a ProductView, its model's id should be stored somewhere (possibly in an array) so that it can be sent back to the server side for processing.
Problem: Where should I store the id of the ProductView that has been clicked by the user? I tried storing it in its parent view ProductListView but cannot seem to access the array selectedProducts in the parent view from the child view ProductView.
Is this the correct approach? How should this be done?
Model
ProductCollection = Backbone.Collection.extend({
model: Product,
url: '/wizard'
});
Parent View
ProductListView = Backbone.View.extend({
el: '#photo_list',
selectedProducts: {}, // STORING SELECTED PRODUCTS IN THIS ARRAY
initialize: function() {
this.collection.bind('reset', this.render, this);
},
render: function() {
this.collection.each(function(product, index){
$(this.el).append(new ProductView({ model: product }).render().el);
}, this);
return this;
}
});
Child View
ProductView = Backbone.View.extend({
tagname: 'div',
className: 'photo_box',
events: {
'click': 'toggleSelection'
},
template: _.template($('#tpl-PhotoListItemView').html()),
render: function() {
this.$el.html(this.template( this.model.toJSON() ));
return this;
},
// ADDS ITS MODEL'S ID TO ARRAY
toggleSelection: function() {
this.parent.selectedProducts.push(this.model.id);
console.log(this.parent.selectedProducts);
}
});
I don't think parent is a property of a backbone View type, and you haven't defined it, so there's no way this line is going to work:
this.parent.selectedProducts.push(this.model.id);
It seems like the correct approach would be to add a selected property to the Product model; toggle that property in the click handler. Then, when it's time to submit to the server, collect the IDs by filtering the Products collection for selected items (underscore.js included with Backbone makes this easy).
Why not try to keep selected information, directly in model. So, you will be easily tracking change state of selected using events, and use that information on further wizard steps?
toggleSelection: function () {
this.model.set({ selected: true });
}