Ember 2 - Hide / show content component - ember.js

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.

Related

Delete a record in Ember data not working

I have a table (a component) and a delete button in each row. When the the delete button is clicked the specific row should be deleted.
Tried the following code:
MyComponent.js
import Ember from 'ember';
export default Ember.Component.extend({
actions:{
deleteCartRecord(cartDetails){
debugger;
this.sendAction('deleteRecord',cartDetails);
}
}
});
In MyComponent.hbs
{{#each model.orderParts as |newCart|}}
<div class="card-wrapper col-lg-12 col-md-12">
<div class="col-lg-2 col-md-2">
<div class="order-id">{{newCart.partNumber}}</div>
{{#if (gte newCart.promiseQty newCart.quantity)}}
<div class="order-status delivered">{{env.APP.StockAvailable}}</div>
{{else}} {{#if (gt newCart.promiseQty '0'(and (lt newCart.promiseQty newCart.quantity)))}}
<div class="order-status intransit">{{env.APP.LowInStock}}</div>
{{else}} {{#if (eq newCart.promiseQty '0')}}
<div class="order-status outofstock">{{env.APP.OutofStock}}</div>
{{/if}} {{/if}} {{/if}}
</div>
<div class="col-lg-3 col-md-3">
<div class="item-header">Delivery Date</div>
<div class="item-data">{{newCart.deliveryDate}}</div>
</div>
<div class="col-lg-2 col-md-2">
<div class="item-header">Required Qty.</div>
<div class="item-data">
{{increse-required-quantity incresedQuantity=newCart.quantity}}
</div>
</div>
<div class="col-lg-2 col-md-2">
<div class="item-header">Unit Price</div>
<div class="item-data">{{newCart.unitPrice}}</div>
</div>
<div class="col-lg-2 col-md-2">
<div class="item-header">Total Price</div>
<div class="item-data">{{newCart.partTotalPrice}}</div>
</div>
<div class="col-lg-1 col-md-1 button-colum"><button type="button" class="btn btn-danger" {{action "deleteCartRecord" newCart}}>Delete</button> </div>
</div>
{{/each}}
My Controller
import Ember from 'ember';
export default Ember.Controller.extend({
actions:{
deleteRecord(data){
debugger;
let confirmation = confirm("are you sure to delete");
if(confirmation)
{
debugger;
data.deleteRecord();
data.save();
}
}
}
});
The template file in which component is called
<hr>
</div>
<div class="col-lg-12 col-md-12">
<div class="checkout-summery-wrapper">
<div class="total-label">Total</div>
<div class="total">{{model.totalPrice}}</div>
<!--<div class="tax-text">( Inclusive of all taxes )</div>-->
<div class="place-order-button"><button type="button" class="btn siemens-btn">Place Order</button></div>
</div>
</div>
<div class="col-lg-12 col-md-12">
{{#if model.orderParts.isGeneric}}
<div class="panel panel-default card-list-panel">
<div class="panel-heading-cart col-lg-12 col-md-12">
<div class="col-lg-11 col-md-11 heading">Generic Parts</div>
<div class="col-lg-1 col-md-1">Delete All</div>
</div>
<div class="panel-body">
{{cart-record model = model}}
</div>
</div>
{{/if}}
{{#unless model.orderParts.isGeneric}}
<div class="panel panel-default card-list-panel">
<div class="panel-heading-cart col-lg-12 col-md-12">
<div class="col-lg-11 col-md-11 heading">Hot Gas Path</div>
<div class="col-lg-1 col-md-1">Delete All</div>
</div>
<div class="panel-body">
{{cart-record model = model deleteRecord=(action 'deleteRecord')}}
</div>
</div>
{{/unless}}
</div>
MyRoute
import Ember from 'ember';
export default Ember.Route.extend({
model: function()
{
return this.get('store').queryRecord('cart',{userId:1})
}
});
My Serializer
import DS from 'ember-data';
export default DS.JSONSerializer.extend(DS.EmbeddedRecordsMixin, {
primaryKey: 'totalPrice',
attrs: {
orderParts:
{
serialize: 'records',
deserialize: 'records'
}
}
});
I have the following issues:
In MyComponent.hbs, will newCart being passed as a parameter delete all the records or the specific record I want deleted?
Any ideas on why MyController is not invoked from the component?
Is this the correct way of deleting a record in ember?
Thank you in advance.
In MyComponent.hbs newCart is passed as a parameter will this delete all the record or the specific record i want?
It will delete the particular record alone. if you want to delete all the record then you can try unloadAll('model-name')
MyController is not invoked from the component why is that?
You need to send action upcon calling component, {{my-component deleteRecord=(action 'deleteRecords') }} . Actually real problem is, you are calling deleteRecord but in controller you got deleteRecords.
Is this the correct way of deleting a record in ember?
If you want to delete right away then you can use destroyRecord this will delete and save record immediately
Well, your example is full of bugs...
In MyComponent.hbs, will newCart being passed as a parameter delete all the records or the specific record I want deleted?
Nope.
Firstly, you need to understand that the result of store.query in your route returns a DS.ManyArray(an Array like object, which is model in your example) contains group of DS.Model instances (which should be newCart in your example, but you must change to {{#each model as |newCart|}} first). And only this DS.Model has method .save() and .deleteRecord().
The action you set on the button is {{action "deleteCartRecord" newCart.partNumber}}, so you actually passing a property called partNumber to deleteRecord and running deleteRecord and save on this property. Unless this partNumber is a DS.belongsTo pointing to another DS.Model, or it cannot work at all.
But what you wanted is to delete newCart, right?
Any ideas on why MyController is not invoked from the component?
Your invoke is right. But since your component is full of bugs, it must be throwing exceptions somewhere else and the app cannot run already.
Is this the correct way of deleting a record in ember?
I think I answered enough in the first question.

Ember template does not remove when transition to different route

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

How to set itemController for instance variable in ember.js indexController?

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'
});

bindAttr reverse boolean

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')
});

In EmberJS my event triggers all the sub-views instead of just the targeted one

i'm learning EmberJS and building a comment section that allows 1 level of sub comments. I have an Ember View listing all the comments, when you click "reply" on a particular comment, it should display a textarea input for a user to write a sub-comment.
In my EmberJS code when you click "reply" it shows the textarea input for all the comments not just the specific one. Any advice would be appreciated :)
// View
App.commentsView = Em.View.create({
templateName: 'commentsTmpl',
showReply: false,
reply: function(e) {
e.view.set('showReply', true);
e.preventDefault();
}
});
App.replyCommentsView = Em.View.extend({
showReplyBinding: 'App.commentsView.showReply'
});
// Template
<script data-template-name="commentsTmpl" type="text/x-handlebars">
</h2>comment</h2>
{{#each App.commentsController}}
<div class="comment-group clearfix">
<div class="comment">
<img class="comment-pic" {{bindAttr src="userPic"}} alt="user pic">
<div class="comment-content">
{{userName}}
<span class="comment-body">{{text}}</span>
<div class="comment-actions clearfix">
<a href="#" {{action "reply"}}>Reply</a>
</div>
</div>
</div>
{{#view App.replyCommentsView}}
{{#if showReply}}
<div class="comment-reply">
<h2>sub-comment</h2>
<textarea class="txt-comment-reply" rows="2" cols="65"></textarea>
</div>
{{/if}}
{{/view}}
</div>
{{/each}}
</script>
Currently you are binding the showReply to App.commentsView which is the whole container. To be make it easy activate single comments, I'd suggest looking into a CollectionView, this way each of your comments will have their own view and you can toggle showReply on an individual comment's view.
Something like this: (Sorry, I haven't tested it)
App.commentsView = Em.View.create({
templateName: 'commentsTmpl'
});
App.CommentView = Em.View.extend({
classNames: "comment-group clearfix".w(),
showReply: false,
reply: function(e) {
e.preventDefault()
this.set("showReply", true)
}
})
// Template
<script data-template-name="commentsTmpl" type="text/x-handlebars">
</h2>comment</h2>
{{#collection contentBinding="App.commentsController" itemViewClass="App.CommentView"}}
<div class="comment">
<img class="comment-pic" {{bindAttr src="content.userPic"}} alt="user pic">
<div class="comment-content">
{{content.userName}}
<span class="comment-body">{{content.text}}</span>
<div class="comment-actions clearfix">
<a href="#" {{action "reply"}}>Reply</a>
</div>
</div>
</div>
{{#if showReply}}
<div class="comment-reply">
<h2>sub-comment</h2>
<textarea class="txt-comment-reply" rows="2" cols="65"></textarea>
</div>
{{/if}}
{{/each}}
</script>