I have a component app/components/offer-listing.js:
import Ember from 'ember';
export default Ember.Component.extend({
isOfferShowing: false,
actions: {
offerShow() {
if (this.get('isOfferShowing')) {
this.set('isOfferShowing', false);
} else {
this.set('isOfferShowing', true);
}
}
}
});
and his template app/templates/components/offer-listing.hbs:
<div class="offer__container">
<div class="row">
<div class="gr-3">
<div class="offer__avatar" style="background-image: url('{{ offer.avatar }}')"></div>
</div>
<div class="gr-9">
<div class="offer__name" {{action "offerShow"}}>{{ offer.firstname }} {{ offer.lastname }}</div>
<div class="offer__age" {{action "offerShow"}}>{{ offer.age }} ans</div>
{{#if isOfferShowing}}
<div class="offer__description" {{action "offerShow"}}>{{offer.description}}</div>
{{else}}
<div class="offer__description" {{action "offerShow"}}>{{word-limit offer.description 50}}</div>
{{/if}}
{{#if isOfferShowing}}
<div class="+spacer"></div>
<a class="offer__button"><i class="fa fa-envelope"></i> Contacter par email</a>
<a class="offer__button"><i class="fa fa-phone"></i> Voir le numéro de téléphone</a>
{{/if}}
</div>
</div>
</div>
which is rendered in app/templates/index.hbs:
{{#each model as |offerUnit|}}
{{offer-listing offer=offerUnit}}
{{/each}}
The example is working great, however I would like to hide every "more" content when a new one is showing.
A working solution for this is available here : Using Ember component's methods inside template
Basically, either you keep a reference to the selected element in your controller and pass it to each of your offer-listing components. This way they could compare themselves with this reference to known if they need to be displayed or not.
Or you set a flag in each of your offer model depending on whether is needs to be displayed or not.
I feel as if this is a very simple problem to fix I am just not aware of how to fix it. Currently I have an outlet that displays a template like this:
user.hbs:
<div id="user-profile">
<img {{bind-attr src="avatarURL"}} alt="User's Avatar" class="profilePic"/>
<h2>{{name}}</h2>
<span>{{email}}</span>
<p>{{bio}}</p>
<span>Created {{creationDate}}</span>
<button {{action "edit"}}>Edit</button>
{{outlet}}
</div>
The template to be rendered at the outlet is this:
user_edit.hbs:
<div id="user-edit">
<h3>Edit User</h3>
<div class="panel-body">
<div class="row">
<label class="edit-user-label">Choose user avatar</label>
{{input class="form-control" value=avatarUrl}}
</div>
<div class="row">
<label>User name</label>
{{input class="form-control" value=name}}
</div>
<div class="row">
<label class="edit-user-label">User email</label>
{{input class="form-control" value=email}}
</div>
<div class="row">
<label class="edit-user-label">User short bio</label>
{{textarea class="text-control" value=bio}}
<div>
<div class="row">
<button {{action "save"}}>SAVE</button>
<button {{action "cancel"}}>CANCEL</button>
</div>
</div>
</div>
When I first visit the user route, the outlet does not display because the button has not been clicked. The button is hooked to a controller which takes care of the action. The action just transitions to the route where the template is displayed at the outlet. It appears just as expected but when I click on a different user model, the outlet from the previous user is still there without everything in the <div class="panel-body"> </div>. So Ember hides the panel-body div on transition but not the user-edit div. If you need more information I will be happy to provide it.
Here are the controllers:
userController:
App.UserController = Ember.ObjectController.extend({
actions: {
edit: function() {
this.transitionToRoute('user.edit');
}
}
});
Here is the userEditController:
App.UserEditController = Ember.ObjectController.extend({
actions: {
save: function() {
var user = this.get('model');
user.save();
this.transitionToRoute('user', user);
},
cancel: function() {
var user = this.get('model');
this.transitionToRoute('user', user);
}
}
})
Hi why dont you use {{#link-to 'edit' model}} instead of action ???
you can pass model to link-to so you dont have to get model in controller and then transitionToRoute
Look at this
I have a Ember.js page that displays a table of items and shows details of one of the items when it is selected. The controller looks like this:
CV.IndexController = Ember.ArrayController.extend({
itemController: "CurrVitae",
selectedCurrVitae: false,
actions: {
selectCurrVitae: function(currVitae) {
this.set('selectedCurrVitae', currVitae)
}
}
});
And the index controller is used in a template like this:
<div class="container">
<div class="curr-vitae-list">
{{#each curr_vitae in controller}}
<div class="curr-vitae-item row">
<div class="col-sm-2" {{action selectCurrVitae curr_vitae}}>
{{curr_vitae.name}}
</div>
<div class="col-sm-2">
<!-- NOTE: This method is defined on the item
controller (not the model) so the itemController is
available at this point. -->
{{curr_vitae.createdAtDisplay}}
</div>
<div class="col-sm-7">
<div class="embedded-cell">
{{curr_vitae.summary}}
</div>
<div class="embedded-cell">
{{curr_vitae.objective}}
</div>
</div>
</div>
{{/each}}
</div>
<div class="curr-vitae-view">
<h2>Details</h2>
<!-- EDIT: I have tried setting this
as {{#if selectedCurrVitae itemController="currVitae" }}
to match the way the #each handles item controllers but that did
not seem to work -->
{{#if selectedCurrVitae }}
<!-- NOTE: Down here, however, the item controller is not available
so I can't use methods defined on the item controller for the
currently selected instance. -->
{{ partial "cv_index_details" }}
{{/if}}
</div>
</div>
Question: The problem I'm running in to is that the itemController I've set in the index controller is not available when rendering the selectedCurrVitae in the cv_index_details.
More details:
Specifically, in the partial I want to reuse a editor component (taken from Noel Rappin's Ember.js book). So if the cv_index_details partial looks like this:
<h3 class="selected_cv">{{selectedCurrVitae.name}}</h3>
<div class="row selected_cv_summary">
<h4>Summary</h4>
{{block-editor emberObject=selectedCurrVitae propName="summary" action="itemChanged"}}
</div>
<div class="row selected_cv_experiences">
<h4>Experiences</h4>
{{#each experience in selectedCurrVitae.experiences itemController="experience"}}
{{ partial "experience_detail" }}
{{/each}}
</div>
So in this template, the itemChanged action is not found for the selectedCurrVitae instance. However, I use the same block-editor component for the experience instance and that works correctly; the itemChanged action defined on the ExperienceController is found.
selectedCurrVitae is outside of the itemController, the itemController is only applied inside the each (on each item, hence the name).
Probably the easiest way to accomplish this will be to reuse the item controller and use render.
Template
{{#if selectedCurrVitae }}
{{ render "cv_index_details" }}
{{/if}}
Controller
CV.CvIndexDetailsController = CV.CurrVitaeController.extend();
Or
Template
{{#if selectedCurrVitae }}
{{ render "currVitae" }}
{{/if}}
View
CV.CurrVitaeView = Ember.View.extend({
templateName: 'cv_index_details'
});
I have a form that creates a record, then transitions to the list of resources for a particular object. However once the record is created, it is not reflected in the list of resources. If I refresh the page, the record is saved in the correct place. I have the ember chrome extension installed and if I look under Resources, then the resource is there pointing to the correct Badge. But if I go to badge first, and look for resources, it is not listed. Any ideas? I would be happy to provide any more information necessary to clarify. Thank you in advance
Create Resource Form Controller and Route
Controller
App.ResourcesCreateController = Ember.ObjectController.extend({
resourceTypes: ["link","file","video"],
needs: ['badge','resourcesIndex'],
actions: {
save: function() {
//Gather the info from the form
var description = this.get('description');
var url = this.get('url');
var type = this.get('type');
var text = this.get('text');
var badge = this.get('controllers.badge').get('model');
//set the data to the model of the route (ResourceCreateRoute)
var resource = this.get('model');
console.log(resource);
resource.set('description',description);
resource.set('url',url);
resource.set('type',type);
resource.set('text',text);
resource.set('badge',badge);
var self = this;
//save the route
var a = resource.save().then(function() {
//if success
//this.get('store').reload();
console.log('%c that resource saved rather nicely','color:green;');
self.transitionToRoute('resources.index',self.badge);
}, function() {
//if failure
console.log('%c Yea boss...that didnt go so hot', 'color:red;');
self.set('isError',true);
});
},
reset: function() {
this.transitionToRoute('resources.index');
}
}
});
Route
App.ResourcesCreateRoute = Ember.Route.extend({
model: function() {
return this.store.createRecord('resource');
}
})
List Resources Route
App.ResourcesRoute = Ember.Route.extend({
model: function(){
return this.modelFor('badge').get('resources');
}
});
Models
Resource Model
App.Resource = DS.Model.extend({
'badge': DS.belongsTo('badge'),
'text': attr('string'),
'url': attr('string'),
'description': attr('string'),
'type': attr('string')
});
Badge Model
App.Badge = DS.Model.extend({
'category': DS.belongsTo('category'),
'title': attr('string'),
'type': attr('string'),
'ord': attr('number'),
'short_description': attr('string'),
'full_description': attr('string'),
'mentor': DS.belongsTo('employee'),
'parent':DS.belongsTo('badge'),
'icon': attr('string'),
'required': attr('boolean'),
'signoff_role': attr('string'),
'created_at': attr('string'),
'updated_at': attr('string'),
'resources': DS.hasMany('resource', { async: true } ),
'quiz': DS.belongsTo('quiz', { async: true } )
});
Templates
List of Resources
{{#link-to "resources.create" class="btn btn-primary btn-xs pull-right"}} Create Resource {{icon "plus"}}{{/link-to}}
<h3>Resources</h3>
<dl>
{{#each resource in controller}}
{{render resources/resource resource}}
{{else}}
<p class="lead text-muted">There are no resources</p>
{{/each}}
</dl>
Resource Item Template
{{#if isEditing}}
<div {{bindAttr class="controller.isError:alert-danger:alert-info :alert"}}>
<div class="row">
<div class="col col-lg-2">
<small>Type</small>
{{view Ember.Select contentBinding="resourceTypes" classNames="form-control" valueBinding="type"}}
</div>
<div class="col col-lg-10">
<small>Resource Name</small>
{{input valueBinding="text" class="form-control"}}
</div>
</div>
<div class="row">
<div class="col col-lg-12">
<br>
<small>Description</small>
{{textarea valueBinding="description" rows="5" class="form-control"}}
</div>
</div>
<div class="row">
<div class="col col-lg-12">
<br>
<small>URL,File Name, or Vimeo ID</small>
{{input valueBinding="url" class="form-control"}}
</div>
</div>
<hr>
<div class="btn-group">
<div {{action "save"}} class="btn btn-primary">{{icon "floppy-save"}} Save</div>
{{#if confirmDelete}}
<div {{action "delete"}} class="btn btn-danger">{{icon "trash"}} Are You sure?</div>
{{else}}
<div {{action "confirm"}} class="btn btn-danger">{{icon "trash"}} Delete</div>
{{/if}}
</div>
<div {{action "reset"}} class="btn btn-default"> {{icon "ban-circle"}} Cancel</div>
</div>
{{else}}
<div class="btn-group pull-right btn-group-xs">
{{#if view.hover }}
<div {{action "edit"}} class="btn btn-default">{{icon "cog"}}</div>
{{/if}}
</div>
<dt>
<span class="text-muted">{{resource_icon type}}</span> {{text}}
</dt>
{{#if description}}
<dd class="text-muted" style="margin-bottom:1em">
{{markdown description}}
</dd>
{{/if}}
<hr>
{{/if}}
Create Resource Template
<h3>Create Resource</h3>
<div class="row">
<div class="col col-lg-2">
<small>Type</small>
{{view Ember.Select contentBinding="resourceTypes" classNames="form-control" valueBinding="type"}}
</div>
<div class="col col-lg-10">
<small>Resource Name</small>
{{input valueBinding="text" class="form-control"}}
</div>
</div>
<div class="row">
<div class="col col-lg-12">
<br>
<small>Description</small>
{{textarea valueBinding="description" rows="5" class="form-control"}}
</div>
</div>
<div class="row">
<div class="col col-lg-12">
<br>
<small>URL,File Name, or Vimeo ID</small>
{{input valueBinding="url" class="form-control"}}
</div>
</div>
<hr>
<div {{action "save"}} class="btn btn-primary">{{icon "floppy-save"}} Save</div>
<div {{action "test"}} class="btn btn">Test</div>
{{#link-to "resources.index" class="btn btn-default" }} {{icon "ban-circle"}} Cancel {{/link-to}}
<br><br>
</div>
Just some general notes first.
With this much code, everyone's going to have a much easier time helping you if you provide a JSBin or something. It's a bit of extra work for you, but you're asking for help, and this is a lot to just mentally parse and run. Personally, it was some extra overhead for me because you didn't include your router, so I had to do a pass just to try to figure out how badge and resource were related.
When you're using an ObjectController with the route model set to a new record, with input helpers, you shouldn't need to do all of that setting. That's why you specified those value bindings on the helpers. But when you do need to set a bunch of properties, you can just do that all at once with something like record.setProperties({prop1: prop1Value, prop2: prop2Value ...}); and save yourself a lot of typing.
I don't understand why you're using resourcesIndex as a ResourcesCreateController need. To actually answer your question, it might work to specify just 'resources' as a need
then use something like
resource.save().then(function(record){
self.get("controllers.resources").pushObject(record);
self.transitionToRoute("resources.index", badge); // I don't know if this makes any sense because you didn't show your router, but at the very least, don't use self.badge, or even self.get("badge"), because badge is already accessible in this scope.
}
It'd be nice to see your Model definitions, and even better if you had a jsbin setup showing the problem.
You could always try hooking it up on createRecord.
App.ResourcesCreateRoute = Ember.Route.extend({
model: function() {
return this.store.createRecord('resource', {badge: this.modelFor('badge')});
}
})
It seems like when you create the new resource, you aren't putting it in the store. You should try something like this.store.createRecord(resource) then instead of your resource.save, do a this.store.commit.
I'm not entirely sure I'm right. But it may be a possibility.
Fixed. I am not sure if this is the correct way to handle this but basically the parent model needed to be reloaded once the child model was created and saved. So like this
save: function() {
var self = this;
var resource = this.store.createRecord('resource',{
property: 'property',
relatedProperty: this.get('model'),
//etc
});
resource.save().then(function(){
self.get('model').reload();
},function(){
//do stuff because the resource didnt save
});
}
In my view I have a button which I want to be disabled when the data of the model is not dirty.
When I output: <button {{ action "update" }} {{bindAttr disabled="isDirty"}}>Save</button> it is enabled when the data is not dirty, and disabled when the data is dirty.
I want to reverse that so I tried this: <button {{ action "update" }} {{bindAttr disabled="!isDirty"}}>Save</button> but now is doesn't check the dirtyness at all. How do I reverse the boolean within a bindAttr helper?
Here is my code:
Controller
App.SiteController = Em.ObjectController.extend({
isNotDirty: function() {
return !this.get('isDirty');
}.property('content')
});
App.SiteEditController = Em.ObjectController.extend({
needs : [ 'site' ]
});
Template
{{#with controllers.site}}
<div>{{ isDirty }}</div>
<div>{{ isNotDirty }}</div>
<div class="control-group">
<label class="control-label" for="name">Name</label>
<div class="controls">
{{view Ember.TextField valueBinding="name"}}
</div>
</div>
<div class="form-actions">
<button {{bindAttr disabled="isNotDirty"}} class="btn btn-primary">Save</button>
</div>
{{/with}}
I outputted the isDirty an isNotDirty property so I could watch them change. But only the isDirty property changes when I change the value of the text-field,
The solution can be found in the API-docs of Ember. There is a list of all available shorthand computed helpers.
I used Ember.computed.not for this case:
App.SiteController = Em.ObjectController.extend({
isNotDirty: Ember.computed.not('isDirty')
});