I'm trying to open a modal dialog form in Ember - I have a form that I'd like users to submit a comment to before processing a workflow, but so far I've been unable to find any good documentation on how to open a modal dialog (where to put controllers, routes, models, etc.). I am new to Ember, so please bear with me. Thanks.
I followed Ember Guru's guide called "Master you're modals" on Ember, and they work great!
create template here:
create component called my-modal. Add the following in the components js:
export default Ember.Component.extend({
actions: {
ok: function() {
this.$('.modal').modal('hide');
this.sendAction('ok');
}
},
show: Ember.on('didInsertElement', function() {
Ember.on('hidden.bs.modal', function() {
this.sendAction('close');
}.bind(this), this.$('.modal').modal());
})
});
Add the following code to the components hbs (template):
<div class="modal fade" data-backdrop="static">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h2 class="modal-title text-center">{{title}}</h2>
</div>
{{yield}}
<div class="modal-footer">
</div>
</div>
</div>
</div>
Now you need to create a template for the specific use of the modal where you call the component. Call it: waitingModal or loadingModal or whatever you like. In this modal template you call the component like so:
{{#my-modal title='Loading Modal' ok='save' close='removeModal'}}
{{/my-modal}}
Add the following to the application route:
showModal: function(name, model) {
this.render(name, {
into: 'application',
outlet: 'modal',
model: model
});
},
Lastly to call a modal after clicking a button, in the button's action you need to call:
this.send('showModal','loading-modal');
where loading-modal is the specific template you're calling.
and to remove the modal:
this.send('removeModal');
Hope this helps!
You could use an addon or implement the recipe from the cookbook.
I'm using ember-dialog. Just one command ember install ember-dialog and dialogs on aboard. Here is example of use you're looking for:
export default Ember.Controller.extend({
// Injecting dialog service
dialog: Ember.inject.service(),
model: { username: "Something" },
actions: {
showForm: function() {
this.get("dialog").blank("path-to-some-form", this, {
acceptHandler: "save"
});
},
save: function(presenter) {
var isValid = true;
if (isValid) {
// Closing presenter
presenter.accept();
}
}
}
});
Template (/app/templates/path-to-some-form.hbs):
<form {{action "accept" on='submit'}}>
{{!-- contextObject here is controller --}}
{{input value=contextObject.model.username}}
<button class="w-btn w-btn__default" type="submit">Send</button>
<button class="w-btn w-btn__danger" type="button" {{action "decline"}}>Cancel</button>
</form>
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 the following controller code:
var App = Ember.Application.create();
App.DocumentController = Ember.Controller.extend({
actions:{
test:function(){
console.log('test');
}
}
});
This the form:
div class="form-horizontal form-group form-group-lg row">
<div class="col-xs-10 col-xs-offset-1 col-sm-6 col-sm-offset-1 col-md-5 col-md-offset-2">
{{input type="text" value=name class="form-control" placeholder="Name of document." autofocus="autofocus"}}
</div>
<div class="col-xs-10 col-xs-offset-1 col-sm-offset-0 col-sm-4 col-md-3">
<button class="btn btn-primary btn-lg btn-block" {{action 'test'}}>Create Document</button>
</div>
</div>
{{#if responseMessage}}
<div class="alert alert-success">{{responseMessage}}</div>
{{/if}}
When clicking the button, the debugger outputs:
Nothing handled the action 'firstAction'. If you did handle the action, this error can be caused by returning true from an action handler in a controller, causing the action to bubble
Not sure what the problem is - the route code:
Router.map(function() {
this.resource( 'index', { path: '/' } );
this.route('decisions');
this.route('teams');
this.route('about');
this.resource("documents",{ path: '/documents' }, function() {
this.route('/show', {path: '/show/:id'});
this.route('/edit', {path: ':id/edit'});
});
});
The model code:
import DS from 'ember-data';
export default DS.Model.extend({
name: DS.attr('String'),
url: DS.attr('String'),
text: DS.attr('String')
});
I know ember has a strict naming convention, but I generated the resource (documents) from the ember-cli using the naming convention in their tutorials. How do I get the template hbs code to talk to the controller?
The issue was with my folder structure. Because the document model will have alot of different templates, I decided to add a document folder inside the template folder. I should have done the same with the controller folder (i.e. added a documents folder). Also the file name of the controller needs to be the same file name as the template (in this case index.hbs for template, and index.js for controller file).
The name of the action in your controller is test but you are invoking a method by the name firstAction, which is not defined.
try the below:
<button class="btn btn-primary btn-lg btn-block" {{action 'test'}}>Create Document</button>
I'm new with Ember and i try to make a simple CRUD.
I want a single template for adding and editing of an object.
This is my code :
this.route('foos', {path: '/foos_path'}, function() {
this.route('edit',{path: '/edit/:foo_id'});
this.route('add',{path: '/add'});
this.route('index');
});
The add function work great but i can't make working the edit function.
This is my edit route.
import Ember from 'ember';
export default Ember.Route.extend({
title : '',
model: function(params) {
this.store.find('foo', params.foo_id).then(function(foo) {
console.log(this, this.get('title'));
this.set('title', foo.title);
});
},
renderTemplate: function() {
this.render('foos.add', {
into: 'foos',
controller: 'foos.add'
});
this.render('foos/add');
}
});
Any help would be great :)
what you need is to extend your adding controller like this, instead of using the same route.
import Ember from 'ember';
import Controller from 'dir/controllers/projects/editController';
// Add controller
export default Controller.extend({
});
the edit and add template could both look like this
{{!-- your add controller can overwrite your editController objects --}}
{{view 'navbar' navbarParams=controllerRelatedObject}}
{{partial "someform"}}
<button {{action 'makeEdit' object1 object2}} class="btn btn-default"> Save </button>
And the edit controller would be handling the makeEdit
// Edit controller
actions: {
makeEdit : function(givenObject, wantedObject){
Ember.RSVP.all(givenObject.invoke('save)).then(...)
},
}
Hope this helps.
Sorry for the delay and thank for you answer. This is how i've achieved my goal :
AddRoute :
import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
return this.store.createRecord('foo');// This line is need to load a clean model into the template
},
});
EditRoute :
import Ember from 'ember';
export default Ember.Route.extend({
controllerName : 'foos.add', // Defines addController for edit route
templateName : 'foos.add', // Defines AddTemplete for edit route
model: function(params) {
return this.store.find('foo', params.foo_id); // Loads the foo object inside the template
}
});
My addTemplate looks like this :
<div class="row">
<form class="col s12">
<div class="row">
<div class="input-field col s12">
{{input placeholder="Foo name" id="foo_name" type="text" class="validate" value=model.title}}
<label for="foo_name"></label>
</div>
<div class="row">
<button class="btn waves-effect waves-light col s12" type="submit" name="save" {{action 'add'}}>Submit
<i class="mdi-content-send right"></i>
</button>
</div>
</div>
</form>
</div>
And in my controller, i define the save action (Can be defined in route instead):
import Ember from 'ember';
export default Ember.Controller.extend({
actions: {
save: function() {
// The following is not needed now because we load a record on add and edit route.
/*var foo = this.store.createRecord('foo', {
title : this.get('title')
});*/
// We can instead save the record directly
this.get('model').save().then(function() {
console.log('Foo save.');
}).catch(function(error) {
console.log('Error : ' + error);
});
},
}
});
I hope this will help someone.
I'm using Ember, and I have a box that the user can click and it will pop open a modal window with more info.
I also have a button on that box that performs a different action.
If I click on the button, the correct action is triggered, but it also triggers the action for the encompassing <div>. How do I prevent that action on the <div>, while allowing the action from the <button>?
Here's my template:
<div {{action 'openModal' 'productResultOverview' this}} class="simpleproducttile">
<div class="row simpleproducttitle">
<p>{{ProductId}}<i class="icon-export helpiconleft"></i><img src="img/largebox.png"></img></p>
</div>
<div class="row simpleproductdescription">
<p>{{ProductDescription}}</p>
</div>
<div class="medium primary btn customizebutton">
<input type="submit" {{action "sendToSavedItems"}} value="Select and Customize"/>
</div>
</div>
Edit:
Action for the button:
App.ProductResultController = Ember.ObjectController.extend({
actions: {
sendToSavedItems: function() {
console.log('sending to saved items');
}
}
});
And the action openModal is contained within ApplicationRoute:
App.ApplicationRoute = Ember.Route.extend({
id: 'modal',
actions: {
openModal: function (modalName, model) {
console.log('opening modal', modalName, model);
return this.render(modalName, {
into: 'application',
outlet: 'modal',
model: model
});
},
//....
});
From the Ember.js docs:
By default, the {{action}} helper allows events it handles to bubble up to parent DOM nodes. If you want to stop propagation, you can disable propagation to the parent node.
What you should do is add bubbles=false to the inner items:
<input type="submit" {{action "sendToSavedItems" bubbles=false}} value="Select and Customize"/>
This should stop the click from propagating to the parent <div>.
Just when I think that I'm getting a handle on ember, this happens and I run into a brick wall.
I have
App.Router.map(function() {
this.resource('customers', function() {
this.resource('customer', {
path : ':customer_id'
});
customers route :
App.CustomersRoute = Ember.Route.extend({
model: function() {
return this.store.find('customer');
},
customer controller :
App.CustomerController = Em.ObjectController.extend({
needs: ['state'],
isEditing: false,
isNotEditing: Ember.computed.not('isEditing'),
actions : {
startEditing: function() {
this.set('isEditing',true);
this.set('validationErrors', '');
},
save: function() {
and these templates :
<script type="text/x-handlebars" data-template-name="customers">
...
{{outlet}}
...
</script>
<script type="text/x-handlebars" data-template-name="customer">
...
{{#if isEditing}}
<div class="well well-sm">
<a class="btn btn-success" {{action save}}>Save</a>
<a class="btn btn-warning" {{action cancel}}>Cancel</a>
</div>
{{else}}
<div class="well well-sm">
<a class="btn btn-primary" {{action startEditing}}>Edit</a>
<a class="btn btn-danger" {{action delete}}>Remove</a>
</div>
{{/if}}
this is all working well for me. I can select a customer, press the edit button, form inputs get enabled, press the save button and changed data is persistent back to the db
however : this is my brick wall.
How do I enable a feature to create a new record : I dont want to duplicate the edit form, just show blanks
I am assuming that I need to put "new" into the router map
this.resource('customers', function() {
this.resource('customer', {
path : ':customer_id'
});
route('new');
});
but do I create a CustomerNew controller and CustomerNew route and CustomerNew template ?
I have put an action into the customers controller
<a class="btn btn-primary btn-xs pull-right" {{action startNew}}>New</a>
do I really need to create a route & controller & template just to handle the new action ? Or can I reuse the customer/1 route/controller/template ?
thanks
To do things The Ember Way (TM) you'd want to use a new route, controller, and template. Ember does make it easy to consolidate your logic and not have to duplicate code. In templates you can do this with partial, and in your JS code you can do it with Ember.Mixin.
Here's a JSBin of the general idea : http://jsbin.com/ucanam/1056/edit
Here's the interesting parts of the templates :
<script type="text/x-handlebars" data-template-name="customers/new">
Add a new customer<br/>
{{partial "customers/form"}}
</script>
<script type="text/x-handlebars" data-template-name="customer/edit">
Edit a customer<br/>
{{partial "customers/form"}}
</script>
<script type="text/x-handlebars" data-template-name="customers/_form">
{{input value=name}}
<button {{action save}}>Save</button>
<button {{action cancel}}>Cancel</button>
</script>
And then the controllers :
App.CrudController = Ember.Mixin.create({
actions : {
save : function(){
this.get('model').save();
this.transitionToRoute('customers');
},
cancel : function(){
this.get('model').rollback();
this.transitionToRoute('customers');
}
}
});
App.CustomersNewController = Ember.ObjectController.extend(App.CrudController,{});
App.CustomerEditController = Ember.ObjectController.extend(App.CrudController,{});