well i need to insert a component when the user click on button, my code:
dash.hbs
<button class="btn btn-primary" {{action 'solisXTax'}}> Consul</button>
dash.js //controller
actions:{ solisXTax(){ "theCode" }, }
and my componenet is ember-chart,
{{ember-chart type="Bar"
data=solsGraph
width=500 height=350
options=opcionesGrafica
legend=true}}
Thanks
I don't know if you are familiar with the handlebars conditionals, but you should read more about that in the guides
You can use a conditional like so:
//templates/application.hbs
<button class="btn btn-primary" {{action 'solisXTax'}}> Consul</button>
<hr/>
{{#if componentVisible}}
{{ember-chart}}
{{else}}
no component shown
{{/if}}
with the corresponding action in your controller
//controllers/application.js
export default Ember.Controller.extend({
componentVisible: false,
actions:{
solisXTax(){
this.toggleProperty('componentVisible')
}
}
});
Here is a twiddle that showcases using an if statement to toggle your component.
You could also dynamically toggle between different components, where one could be a empty component, but that might be overkill for your use case.
Related
application.hbs
<div class="mdl-layout mdl-js-layout mdl-layout--fixed-header">
{{navigation-bar class="mdl-layout__header"}}
{{#if Auth.authenticated}} //Auth is a service
{{navigation-drawer class="mdl-layout__drawer"}}
{{/if}}
<main class="mdl-layout__content">
<div class="page-content">
{{outlet}}
</div>
</main>
</div>
navigation-drawer.hbs
<span class="mdl-layout-title">Title</span>
<nav class="mdl-navigation">
{{#if Auth.authenticated}}
<span>Hi {{name}}</span>
<button type="button" name="logout">Logout</button>
{{else}}
{{#link-to 'login'}}Login{{/link-to}}
{{/if}}
</nav>
navigation-drawer.js
import Ember from 'ember';
/* globals componentHandler */
export default Ember.Component.extend({
Auth: Ember.inject.service('Auth'),
init() {
this._super(...arguments);
if(this.get('Auth').authenticated) {
this.set('name', 'lokesh');
}
},
didInsertElement () {
this._super(...arguments);
[].forEach.call(this.$(), function (el) {
componentHandler.upgradeElement(el);
});
}
});
navigation-bar.hbs
<div class="mdl-layout__header-row">
<span class="mdl-layout-title">Title</span>
<div class="mdl-layout-spacer"></div>
<nav class="mdl-navigation mdl-layout--large-screen-only">
{{#if Auth.authenticated}}
<button type="button" name="logout" {{action 'logout'}}>Logout</button>
{{else}}
{{#link-to 'login'}}Login{{/link-to}}
{{/if}}
</nav>
</div>
navigation-bar.js
import Ember from 'ember';
export default Ember.Component.extend({
Auth: Ember.inject.service('Auth'),
actions: {
logout() {
this.get('Auth').logout();
}
}
});
Login route
import Ember from 'ember';
export default Ember.Route.extend({
Auth: Ember.inject.service('Auth'),
actions: {
authenticate() {
this.get('Auth').login();
this.transitionTo('/');
}
}
});
<h3>Login Page</h3>
<button type="button" name="button"{{action 'authenticate'}} class="mdl-button mdl-js-button mdl-button--raised mdl-button--colored">Login</button>
What i am trying to do?
I have a application route which has a navigation-bar displayed at all time and a navigation-drawer which should be displayed only if the user is logged in. so i wrote a {{#if Auth.authenticated}} condition to hide navigation-drawer. once user click login button in login route i am updating the AUTH service which is used across all the files. once user click login, he will routed to application route again. This time the condition {{#if Auth.authenticated}} will be true and navigation-drawer should show up. i checked in DOM. it has drawer. But the drawer button is not showing. When i refresh the page, it showed up. so i understood, material is acting on those components only during the onload time. However i understood that componentHandler.upgradeElement() is useful in case dynamic compoenents. i tried it in navigation-drawer.js. But it didn't work. Where i went wrong?
Just before upgradeElement, you may want to downgrade nodes under layout or immediate parent and them upgrade them. That way all the nodes get reset and load correctly. This worked for me.
componentHandler.downgradeElements(document.querySelector(".mdl-layout"));
componentHandler.upgradeDom();
I have a component priority-selector that looks like this...
export default Ember.Component.extend({
priority: 'low',
didInsertElement: function() {
this.$('[data-toggle="tooltip"]').tooltip();
},
actions: {
click: function(priority) {
this.set('priority', priority);
this.$('.btn-primary').removeClass('btn-primary');
this.$('.btn-for-' + priority).addClass('btn-primary');
}
}
});
Template code...
<div class="pull-right btn-group" role="group">
<div type="button" class="btn btn-default btn-for-low" {{action 'click' 'low'}}>LOW</div>
<div type="button" class="btn btn-default btn-for-medium" {{action 'click' 'medium'}}>MEDIUM</div>
<div type="button" class="btn btn-default btn-for-high" {{action 'click' 'high'}}>HIGH</div>
</div>
I use this component in a template like so...
<div class='col-sm-3'>
{{priority-selector priority=priority_gender}}
</div>
And I have it specified in the controller...
export default Ember.Controller.extend({
...
priority_gender: 'low'
...
})
But the property in the controller never changes when I interact with the component, I can observe this to be the case by looking in the ember inspector.
Your code is working.
So, make sure to specify priority_gender in right controller for template you're using priority-selector component.
I'm trying to DRY up my templates by creating views for common layout elements. I have the following views defined
App.SplitListView = Em.View.extend({
tagName: 'div',
classNames: [ 'panel', 'panel-default' ]
});
App.SplitListHeaderView = Em.View.extend({
classNames: [ 'panel-heading' ],
templateName: 'split-list-header-view-layout'
});
The template for the SplitListView is a simple {{yield}}
The template for the SplitListHeaderView is
<span class="panel-title">{{view.headerText}}</span>
{{#if view.showCreateButton}}
<span class="pull-right">
<button type="button" class="btn btn-primary btn-sm">
<i class="fa fa-plus fa-lg" style="padding-right: 10px;"></i>{{view.createButtonText}}
</button>
</span>
{{/if}}
Then the template for the submodule:
{{#view App.SplitListView}}
{{view App.SplitListHeaderView headerTextBinding="Sandwiches" showCreateButtonBinding=true createButtonTextBinding="Make me a sandwich!"}}
{{/view}}
The desired end result is that I'd like to use the SplitListView and SplitListHeaderView everywhere in my app that I use that layout and just set the relevant bits of text via the controller. But so far it's just not working. I feel like this should be easy to do and I'm just missing something. I found this question which looks to be along the same lines as my question but the solution did not work for me.
Does anyone have any experience with something like this or am I off my rocker in trying to use views in this manner?
I believe you have three options here:
1) Use a component instead of a view. Components are reusable, self-contained 'modules' that allow you to bind properties from the component's context in exactly the same manner as you are trying with your view.
2) If the only thing you're reusing between the current non-DRY templates is the html/handlebars you should use a {{partial}} instead of a view because that doesn't create any scope within the view hierarchy and will allow you to bind the handlebars in the partial directly to route's view/controller properties without specifying additional property bindings or scope.
3) Use a Em.Handlebars.helper to accept three arguments (headerText, buttonText, and showCreateButton) and return a new Handlebars.SafeString('some html string'); or something along those lines.
A solution
If I were you, I would utilize methods 2 and 3 together as follows:
First, use a helper (I'm using a helper with a globally accessible method) instead of App.SplitListView to wrap some html around the buffer (i.e. content) inside of the opening and closing handlebars:
// Handy method you can use across many different areas of the app
// to wrap content in simple html
Utils.wrapBuffer = function(open, close, options, context) {
options.data.buffer.push('\n' + open);
if (options.fn(context)) {
options.data.buffer.push('\n' + options.fn(context));
}
options.data.buffer.push('\n' + close);
}
Then the actual helper
App.Handlebars.helper('split-list', function(options) {
var open = '<div class="panel panel-default">;
var close = '</div>';
Utils.wrapBuffer(open, close, options, this);
});
Now, your template will look like:
{{#split-list}}
// This is where our content will go
{{/split-list}}
This has the advantage of wrapping what is inbetween the handlebars opening and closing tags with html without adding or changing scope. Thus, property bindings will work seamlessly.
Now, replace App.SplitListheaderView with a component set up in a similar manner:
App.SplitListHeaderComponent = Em.Component.extend({
classNames: ['panel-heading'],
headerText: null,
createButtonText: null,
showCreateButton: false,
});
You layout (components use layouts, not templates) will be located at templates/components/split-list-header.hbs. it will look as follows:
<span class="panel-title">{{headerText}}</span>
{{#if showCreateButton}}
<span class="pull-right">
<button type="button" class="btn btn-primary btn-sm">
<i class="fa fa-plus fa-lg" style="padding-right: 10px;"></i>{{createButtonText}}
</button>
</span>
{{/if}}
Note, that properties are something not view.something and each property is declared allowing it to be overwritten in the handlebars helper. Now your submodule's template will look like:
{{#split-list}}
{{split-list-header
headerText='Sandwiches'
showCreateButton=true
createButtonText='Make me a sandwich!'
}}
{{/split-list}}
If you wanted, you could bing these properties to a property on your controller or view instead of writing them in the template every time.
Another Improvement
You could go one step further and scrap the wrapping helper all together because it's not doing anything except adding HTML. In this case, the component would look like {{split-list headerText=blah...}} and would have the following template:
<div class="panel-heading">
<span class="panel-title">{{view.headerText}}</span>
{{#if view.showCreateButton}}
<span class="pull-right">
<button type="button" class="btn btn-primary btn-sm">
<i class="fa fa-plus fa-lg" style="padding-right: 10px;"></i>{{view.createButtonText}}
</button>
</span>
{{/if}}
</div>
I am generating a button group with a template:
<div class="btn-group">
{{#if allowNew}}
{{#link-to newRoute type="button" class="btn btn-primary"}}<i class="fa fa-plus"></i> {{t generic.add}} {{capitalize singularHuman}}{{/link-to}}
{{/if}}
{{#if allowDeleteAll}}
<button type="button" {{action "destroyAllRecords"}} class="btn btn-danger"><i class="fa fa-trash-o fa-lg"></i> {{t generic.delete_all}} {{capitalize pluralHuman}}</button>
{{/if}}
</div>
Ember is placing <script> nodes inside the button group, I imagine to handle binding, view updates or whatever.
The problem is that bootstrap is relying on CSS rules like
.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle)
to finetune the styles. Since the <script> tag is placed before the first <button> or <a> tag, and after the last one, those CSS rules are getting applied where they should not, and instead of having something like this:
I get something like this:
As you can see the stock bootstrap style has rounded corners for the first and last button (the external corners), but my implementation has no such rounded corners.
Is it possible to somehow overcome this problem?
Use an ember view. See https://coderwall.com/p/f1npbg
App.MyView = Ember.CollectionView.extend({
classNames: ['btn-group'],
itemViewClass: Ember.View.extend({
template: Ember.Handlebars.compile("{{view.content.name}}"),
tagName: 'button',
classNames: ['btn']
}),
attributeBindings: ['data-toggle'],
'data-toggle': 'buttons-radio'
});
I have a template that allows me to edit settings of a user. Within the usersettings my model is the actual user. And I have a 1-N Relationship between User-Settings:
{{#if isEditing}}
<button {{action 'doneEditing'}}>Done</button>
{{else}}
<button {{action 'edit'}}>Edit</button>
{{/if}}
<h1>Settings</h1>
{{#each this.setting}}
{{#if isEditing}}
{{name}}: {{view Ember.TextField valueBinding='value'}}
{{else}}
{{name}}: {{value}}
{{/if}}
{{/each}}
The problem is that if I click the Edit button, the "Edit" status is only coming to main controllere here. So my Buttons change, but within the loop, the Textfield is not appearing.
App.UserController = Ember.ObjectController.extend({
isEditing: false,
actions: {
edit: function(){
this.set('isEditing', true);
},
doneEditing: function(){
this.set('isEditing', false);
},
}
});
(I don't have a specific controller for the SettingItems in my Loop)
How do I handle such a case?
Yeah, you definitely can do it.
http://emberjs.jsbin.com/uwENUbeh/3/edit