I am still trying to understand how to properly structure an ember.js application. So, this may be a systemic issue with the way I am trying to solve this. That being said, I am going to try asking the same question a couple different ways ...
In the code example below, when a record is created, how can I get it to be added to the list with the isEditing property set to true?
Can I access to a specific object controller from its array controller?
Each task has a view state and an edit state. When a new task is created, how can I have it initially appear in the edit state?
App.TasksController = Ember.ArrayController.extend({
actions: {
createTask: function(){
var task = this.store.createRecord('task');
task.save();
}
}
});
App.TaskController = Ember.ObjectController.extend({
isEditing: false,
actions: {
toggleEditing: function(task) {
if(this.isEditing){
task.save();
}
this.set('isEditing', ! this.isEditing );
}
}
});
<script type="text/x-handlebars" data-template-name="tasks">
<ul>
{{#each task in controller}}
{{render "task" task}}
{{/each}}
<li {{action "createTask"}} >
New Task
</li>
</ul>
</script>
<script type="text/x-handlebars" data-template-name="task">
<li {{action "toggleEditing" task on="doubleClick"}} >
{{#if isEditing }}
{{textarea value=title cols="80" rows="6"}}
{{else}}
{{title}}
{{/if}}
</li>
</script>
Set the property on the model.
You don't have to define the property as an attr on the model (which means it won't send it up to the server on save etc), but you can set the property on the model.
Or you can do it based on the currentState of the model. (click go to orders, then add orders)
http://emberjs.jsbin.com/AvOYIwE/4/edit
App.OrderController = Em.ObjectController.extend({
_editing: false,
editing: function(){
return this.get('_editing') || (this.get('model.currentState.stateName') == 'root.loaded.created.uncommitted');
}.property('model.currentState.stateName', '_editing'),
actions: {
stopEditing: function(){
// blow away the computed property and just set it to true
this.set('editing', false);
},
startEditing: function(){
this.set('editing', true);
},
}
});
Related
I am working on an Ember app (version 1.11, old one) and have multiple checkboxes build over in loop, code as below.
I need to know if an element has been checked or unchecked in action.
Ember ehbs:
{{#each item in data}}
<label>
<input type="checkbox" checked="isChecked" {{action "getData" item on="change"}}/>
<span>{{item.type}}</span>
</label>
{{/each}}
Ember Component:
var component = Ember.Component.extend({
isChecked: true,
actions: {
getData: function(data){
var state = this.get('isChecked');
var type = data.type;
}
}
})
I thought the variable's "isChecked" value will be maintained for each individual checkbox, but its not the case, it is just one variable for all checkboxes.
So, how can I achieve this OR check individual states for all checkboxes whether its checked or unchecked.
In long run, I am trying to get here - http://emberjs.jsbin.com/qaruviwuze/edit?html,js,output but dont want to access and play with DOM as its done here.
make each checkbox as component
<input type="checkbox" checked="isChecked" {{action "getData" item on="change"}}/>
as follows checkbox-component
{{#each model as |item|}}
{{checkbox-component isChecked=item.isCheked}}
{{/each}}
such that you can retrieve easily each check item value.
export default Ember.Component.extend({
tagName: "input",
isChecked: false,
attributeBindings: ['type', 'checked'],
type: 'checkbox',
checked: function() {
return this.get('isChecked');
}.property('isChecked'),
click: function() {
console.log(this.set('checked', this.$().prop('checked')))
}
});
check https://ember-twiddle.com/996f6408266af8cd4d3372bed8e8331c?openFiles=components.checkbox-component.js%2Ctemplates.components.checkbox-component.hbs
Demo: http://jsbin.com/zexopa/1/edit?html,js,output
I use the query parameters in my application. And the queryParameters are 'name' and 'category'.
The 'name' parameter is used in the select and the 'category' uses the input, but there is something wrong with the select 'name' if I set it default to null.
If I change the 'name', the 'name' always is undefined in the url.
Route:
App.IndexRoute = Ember.Route.extend({
beforeModel: function() {
this.controllerFor('index').set('products', [1,2,3]);
},
model: function() {
return [{'is_active':false, 'name':'One'}, {'is_active':false, 'name':'Two'}, {'is_active':false, 'name':'Three'}, {'is_active':false, 'name':'Four'},{'is_active':false, 'name':'Five'}];
},
actions: {
queryParamsDidChange: function() {
this.refresh();
}
}
});
Controller:
App.IndexController = Ember.Controller.extend({
queryParams: ['name', 'category'],
name: null,
category: null
});
Template:
<script type="text/x-handlebars">
<h2>Welcome to Ember.js</h2>
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="index">
{{view "select" content=products value=name prompt="all"}}
{{input type="text" value=category class="form-control"}}
<ul>
{{#each model as |item|}}
<li>{{item.name}}</li>
{{/each}}
</ul>
</script>
Can you help to check what happens to my application?
Query params must be string to be properly binded. Your input works, as the value is String object. In name array you provided Integer. Unfortunately, I have not found any mention about that in docs, but you can see a working demo here: http://jsbin.com/lixili/1/edit?html,js,output
If I can give you some tip about your code:
beforeModel is not a place for setting controller properties, do it in setupController method as in JSBin provided
You did not defined query params in route, but you could and get rid of the queryParamsDidChange
Hope I helped!
I can't get a the 'show' page of an instance of a model to display its data.
Here's the template that won't show its data:
<template name="priority">
<h1>Priority: {{title}}</h1>
</template>
It's very simple in and of itself, yet I can't get title to display. Iron:router does the job of directing us to this page with the following code:
Router.route('/priority/:_id', function(){
var priority = this.params._id;
this.render('priority', {
data: function(priority){
Meteor.call('showPriority', priority, function(error){
if (error) {
console.log("An error has occured: " + error);
}
})
}
})
}, {
name: 'priority.show'
});
The Meteor.method is very simple, it just queries for the variable priority:
'showPriority': function(priority) {
return Priorities.findOne({_id: priority});
}
The view which carries the href is here:
<template name="priorityList">
<ul class="table-view">
{{#each this}}
<li class="table-view-cell">
{{title}}
<span class="pull-right icon icon-edit"></span>
</li>
{{/each}}
</ul>
</template>
Note that this view shows a list of all priorities. When I inspect the href, all the paths are being dynamically generated with an _id:
<a href="/priority/yYihyZmZ2xkAso7i5">...
Oh, and I should mention, that I also tried to use the waitOn method, since I thought I might be loading the template before the data, but that didn't help either...
Router.configure({
...
waitOn: function(){
return Meteor.subscribe('priorities');
}
});
So much code, just to show what's going on!
What's the deal here? Why won't my "show" template give me any data?
Change you route to this.
Router.map(function () {
this.route('priority', {
path: '/priority/:_id',
waitOn: function(){
return Meteor.subscribe('priorities',this.params._id);
},
data: function(){
if(this.ready()){
return Priorities.findOne({_id: this.params._id});
}else{
this.render('loading') //if data not ready we render some loading template
}
}
});
});
You don't need to make a Meteor.call, for the find(); instead to everything on the data:function()
The above is just an example so you can get the idea, but it should work since you are expecting _id:priority and _id:this.params._id its the same.
Just be sure you have the autopublish package removed.
and have the subscriptions/publish in order.
Meteor.publish('Menu', function(){
return Priorities.find();
});
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.
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>