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.
Related
i have a page with button "CONFIRM NOW" where upon clicking i am getting it to save across network with an JSON REST adapter.
/accounts/chinboon#gmail.com/verification
/accounts/{email}/verification
But i am getting a "model.save is not a function" error.
Uncaught TypeError: model.save is not a function
at Class.verifyEmail (index.js:19)
at Router.triggerEvent (ember.debug.js:28654)
at trigger (ember.debug.js:55917)
at Router.trigger (ember.debug.js:57517)
at Class.send (ember.debug.js:27844)
at Class.send (ember.debug.js:31852)
at ember.debug.js:11639
at Object.flaggedInstrument (ember.debug.js:18475)
at ember.debug.js:11638
at Backburner.run (ember.debug.js:717)
templates/accounts/show/verification/index.hbs:
<section class="main-content">
<div class="container-fluid" style="border:1.0rem solid #fff;">
<div class="row">
<div class="col-xs-12 col-sm-7 col-lg-6">
<form {{action "verifyEmail" on="submit"}}>
<div class="form-group">
<h2>Confirm your email now to complete the registration</h2>
</div>
<div class="mt10">
<button type="submit" class="btn btn-success">CONFIRM NOW</button>
</div>
</form>
</div>
</div>
<div class="row mt20">
<div class="col-xs-12 col-sm-7 col-lg-6">
You are receiving this email because ...
</div>
</div>
</div>
</section>
router.js:
import Ember from 'ember';
import config from './config/environment';
const Router = Ember.Router.extend({
location: config.locationType,
rootURL: config.routerRootURL
});
// page route
Router.map(function() {
this.route('accounts', function() {
// /accounts/new
this.route('new', function () {
//this.route('success');
});
this.route('show', { path: '/:account_id' }, function() {
// /accounts/1/confirmation
this.route('confirmation');
// /accounts/1/verification
this.route('verification', function() {
// /accounts/1/verification/success
this.route('success');
});
});
});
});
export default Router;
routes/accounts/show/verification/index.js:
import Ember from 'ember';
export default Ember.Route.extend({
model: function(params) {
return this.store.createRecord('account-verification');
},
actions: {
/* account email verification */
verifyEmail() {
var model = this.get('model');
var self = this;
<<<<<PROBLEM HERE, "MODEL.SAVE IS NOT A FUCTION">>>>>
model.save().then(function(data) {
// handle success
// route user to success page
}).catch((adapterError) => {
console.log(adapterError);
});
}
}
});
models/account-verification.js:
import DS from 'ember-data';
export default DS.Model.extend({
email: DS.attr('string'),
secretToken: DS.attr('string')
});
this.get('model') inside Route will return model function not the record you created through model method. so either you need to move this action to corresponding controller or use controllerFor and get the model from controller.
Move actions to controllers/accounts/show/verification/index.js file.
import Ember from 'ember';
export default Ember.Controller.extend({
actions: {
/* account email verification */
verifyEmail() {
var model = this.get('model');
var self = this;
model.save().then(function(data) {
// handle success
// route user to success page
}).catch((adapterError) => {
console.log(adapterError);
});
}
}
});
I would like to know what the correct way is to refresh my application route after a login, so that my model is loading correctly. At the moment i only get the desired results after hard refreshing the browser.
My application.js (app/routes/application.js)
import Ember from 'ember';
export default Ember.Route.extend({
beforeModel: function() {
return this.get("session").fetch().catch(function() {});
},
model() {
console.log("Session is:" + this.get("session.uid"));
if(this.get("session.uid")) {
return this.store.find('user', this.get('session.uid')).then(function(user){
return user;
});
}
},
actions: {
accessDenied() {
this.transitionTo('login');
},
logout: function() {
this.get('session').close().then(function() {
this.transitionTo('index');
}.bind(this));
}
},
});
My login.js (app/routes/login.js)
import Ember from 'ember';
export default Ember.Route.extend({
beforeModel: function() {
if(this.get('session.isAuthenticated')){
this.transitionTo('dashboard');
}
},
actions: {
login: function() {
var controller = this.get('controller');
var email = controller.get('userEmail');
var password = controller.get('userPassword');
this.get('session').open('firebase', {
provider: 'password',
email: email,
password: password
}).then(function() {
this.transitionTo('index');
}.bind(this));
}
}
});
The problem takes place in my application.hbs template. In here i'm calling {{model.firstName}} e.t.c.
My application.hbs (app/templates/application.js)
{{#if session.isAuthenticated}}
<div class="sidebar-menu">
<div class="brand">
<strong>Project</strong>Todo
</div>
{{partial 'navbar'}}
</div>
<div class="main-content">
<div class="topbar">
<div class="current-user">
<div class="media-object" data-toggle="example-dropdown">
<div class="media-object-section">
<div class="current-user-image" style='background-image:url({{model.userImage}})'>
</div>
</div>
<div class="media-object-section middle">
{{model.firstName}} {{model.lastName}} <svg role="img"><use xlink:href="/assets/icons.svg#icon-angle-down"></use></svg>
</div>
</div>
</div>
{{#zf-dropdown id="example-dropdown" }}
<ul class="menu vertical">
<li>
{{#link-to 'user'}}My Account{{/link-to}}
</li>
<li>
{{#link-to 'setting'}}View settings{{/link-to}}
</li>
</ul>
<button {{action 'logout'}}>Logout</button>
{{/zf-dropdown}}
</div>
{{#liquid-spacer growDuration=250}}
{{#each flashMessages.queue as |flash|}}
{{flash-message flash=flash messageStyle='foundation'}}
{{/each}}
{{/liquid-spacer}}
{{outlet}}
</div>
{{else}}
{{outlet}}
{{/if}}
Just don't load the model in the application route. Create a subroute 'start' route where you load your model:
Application route
import Ember from 'ember';
export default Ember.Route.extend({
beforeModel: function() {
return this.get("session").fetch().catch(function() {});
},
model() {
if(this.get("session.uid")) {
this.transitionTo('start');
}
this.send('accessDenied');
},
actions: { ... }
});
Application hbs
Your template code if user is not logged in
Start route
import Ember from 'ember';
export default Ember.Route.extend({
beforeModel() {
// You should make a mixin or something for this check, and apply this to all your subroutes
if(!this.get("session.uid")) {
this.transitionTo('application');
}
},
model() {
return this.store.find('user', this.get('session.uid')).then(function(user){
return user;
});
}
});
Start hbs
Your template code if user is logged in
I'm new to emberjs and I'm quite overwhelmed with it. I try to get my model data from model image to be displayed in my template index. I can see the model data in ember inspector in my browser but the template can't print it out with Handlebars.
What I noticed is that my model's records have no id. It is null.
\templates\index.hbs:
<form class="form">
<label for="name">Name: </label>
{{input class="form-control" type="text" value=imgurl}}
{{input class="form-control" type="text" value=title}}
<button class="btn btn-default" {{action "addImage"}}>Press it!</button>
</form>
{{#each image as |image|}}
<p>{{this}}</p>
{{else}}
<p>Nothing!</p>
{{/each}}
\routes\image.js:
import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
return this.store.findAll('image');
}
});
\models\image.js:
import DS from 'ember-data';
export default DS.Model.extend({
imgurl: DS.attr('string'),
title: DS.attr('string')
});
\controllers\index.js:
export default Ember.Controller.extend({
actions: {
addImage: function(imgurl, title) {
var model = this.store.createRecord('image', {
imgurl: this.get('imgurl'),
title: this.get('title')
});
model.save();
}
}
});
\router.js:
import Ember from 'ember';
import config from './config/environment';
var Router = Ember.Router.extend({
location: config.locationType
});
Router.map(function() {
this.route('/', {path: '/index'});
});
export default Router;
I think my route is the problem. According to Ember Inspector there's no model tied to my index route.
In your app > adapters > application.js file, does it say, "DS.RESTAdapter.extend"?
If so, and you don't have a backend, this may be a potential cause. If you're just building a test project, you can follow these steps after installing Ember CLI:
ember new test-image-project
Now change directories (cd) into the test-image-project and do:
ember generate model image
ember generate adapter application
ember generate route index
Now go to app > adapters > application.js and change
import DS from 'ember-data';
export default DS.RESTAdapter.extend({
});
to
import DS from 'ember-data';
export default DS.FixtureAdapter.extend({
});
Now go to app > models > image.js and paste the following code:
import DS from 'ember-data';
var Image = DS.Model.extend({
imgurl: DS.attr('string'),
title: DS.attr('string')
});
Image.reopenClass({
FIXTURES: [
{
id: 1,
imgurl: "www.google.com",
title: "google"
},
{
id: 2,
imgurl: "www.amazon.com",
title: "amazon"
}
]
});
export default Image;
Next do something like this in app > routes > index.js
import Ember from 'ember';
export default Ember.Route.extend({
model: function(){
return this.store.findAll('image');
},
actions: {
addImage: function() {
var controller = this.get('controller');
var imgurl = controller.get('imgurl');
var title = controller.get('title');
return this.store.createRecord('image', {
imgurl: imgurl,
title: title
});
}
}
});
and something like this in app > templates > index.hbs:
<form class="form">
<label for="name">Name: </label>
{{input class="form-control" type="text" value=imgurl}}
{{input class="form-control" type="text" value=title}}
<button class="btn btn-default" {{action "addImage"}}>Press it!</button>
</form>
{{#each model as |image|}}
{{#if image.imgurl}}
<p> {{image.imgurl}} </p>
{{else}}
<p>Nothing!</p>
{{/if}}
{{/each}}
In your template, you should replace {{each image as |image|}} with {{each model as |image|}} and replace {{this}} with {{image.imgurl}} or {{image.title}} depending on your use-case.
This is how my products.index looks:
When I click on "Create product" link. It sends me to /products/new. I see a form there, but I dont submit it, instead I click on the "Cancel" button. I have an action in my controller that redirects me to the products.index page.
actions: {
cancel: function() {
this.transitionToRoute('products.index');
return false;
}
}
In /products, I see:
Which is an empty product... The DB in the API has no products. I refresh the page, and the empty product goes away. Whats going on here?
The full code:
// app/routes/products/index.js
import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
return this.store.find('product');
}
});
// app/routes/products/new.js
import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
return this.store.createRecord('product');
},
});
// app/controllers/select-addresses/new.js
export default Ember.ObjectController.extend({
actions: {
cancel: function() {
this.transitionToRoute('products.index');
return false;
}
}
});
// app/templates/products/index.hbs
<h1>Products index</h1>
<p>{{#link-to 'products.new'}}Create product{{/link-to}}</p>
<ul>
{{#each}}
<li>
{{#link-to 'products.show' this}}<strong>{{name}}</strong>{{/link-to}}
<br />Description: {{description}}
<br />Amount in cents: {{amountInCents}}
<br />{{link-to 'Edit' 'products.edit' this}} ยท <a href="#" {{action "delete" this}}>Delete</a>
<br /><br />
</li>
{{/each}}
</ul>
// app/templates/products/new.hbs
<h1>Add a new friend</h1>
<form {{action "save" on="submit"}}>
<p>
<label>Name:
{{input value=name}}
</label>
{{#each error in errors.name}}
<br />{{error.message}}
{{/each}}
</p>
<p>
<label>Description:
{{input value=description}}
</label>
{{#each error in errors.description}}
<br />{{error.message}}
{{/each}}
</p>
<p>
<label>Amount in cents:
{{input value=amountInCents}}
</label>
{{#each error in errors.amountInCents}}
<br />{{error.message}}
{{/each}}
</p>
<p>
<label>Status:
{{input value=status}}
</label>
{{#each error in errors.status}}
<br />{{error.message}}
{{/each}}
</p>
<input type="submit" value="Save"/>
<button {{action "cancel"}}>Cancel</button>
</form>
{{outlet}}
My guess (as a non-Ember-specialist):
In your Ember.Route.extend, for the model, you issue a call to store.createRecord. By this you add an empty product to the store. When you cancel the form, you do not remove the dummy product from the store, so it is still there when you load the index view.
Use ember-data-route and then you should do
{{#each product in model}}
{{#unless product.isNew}}
{{product.name}}
{{/unless}}
{{/each}}
If you don't want to use ember-data-route, you can always use resetController and do model.deleteRecord().
// app/routes/products/new.js
import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
return this.store.createRecord('product');
},
resetController: function (controller, isExiting) {
var model = controller.get('model');
if (isExiting && model.get('isNew')) {
model.deleteRecord()
}
}
});
For a more in-depth version see what ember-data-route is doing.
So in the product index routes, I changed return this.store.find('product'); to return this.store.find('product', {});
And the problem went away! I am not sure if this is the right approach, but so far no issues!
// app/routes/products/index.js
import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
// Before
// return this.store.find('product');
// After
return this.store.find('product', {});
}
});
UPDATE: Doing this.store.unloadAll('product'); before the transition also seems to work. This seems to be a more natural solution. As per this pull request https://github.com/emberjs/data/pull/1714
However, clicking the back button on the browser, still renders the empty product.
So basically:
// app/routes/products/index.js
import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
return this.store.find('product');
}
});
// app/controllers/select-addresses/new.js
export default Ember.ObjectController.extend({
actions: {
cancel: function() {
this.store.unloadAll('product');
this.transitionToRoute('products.index');
return false;
}
}
});
You can use the deactivate route hook to delete the new (not-persisted) record from the store.
// app/routes/products/new.js
import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
return this.store.createRecord('product');
},
deactivate: function () {
var model = this.modelFor('products.new');
if (model.get('isNew')) {
model.destroyRecord();
}
}
});
When you transition away from the route without persisting the record with .save(), it will destroy the record in the store, and therefore won't be rendered in the template.
I have a resource (Trip) and its routes - trips.index, trips.edit, trips.new. I would like to put a copy of the form in trips/new into application.hbs template so it appears on every page.
The form in trips/new route works but the one in application.hbs doesn't. I get the following errors when I submit the form:
Uncaught Error: Assertion Failed: Cannot delegate set('name', a) to
the 'content' property of object proxy
: its 'content' is
undefined.
Uncaught Error: Assertion Failed: Cannot delegate set('errorMessage',
You have to fill all the fields) to the 'content' property of object
proxy : its 'content' is
undefined.
I have the following code.
application.hbs:
...
{{render "trips/new"}}
...
{{outlet}}
...
templates/trips/new.hbs:
<form {{action "save" on="submit"}} role="form">
<p class="text-danger">{{errorMessage}}</p>
<div class="form-group">
<label for="">Name</label>
{{input class="form-control" value=name}}
</div>
<div class="form-group">
<label for="">Country</label>
{{input class="form-control" value=country}}
</div>
<div class="form-group">
<label for="">Start Date</label>
{{input class="form-control" value=startDate placeholder="YYYY-MM-DD"}}
</div>
<div class="form-group">
<label for="">End Date</label>
{{input class="form-control" value=endDate placeholder="YYYY-MM-DD"}}
</div>
<input type="submit" value="Save" class="btn btn-primary">
<button {{action "cancel"}} class="btn btn-default">Cancel</button>
</form>
controllers/trips/base.js:
import Ember from 'ember';
export default Ember.ObjectController.extend({
isValid: Ember.computed(
'name',
'country',
function() {
return !Ember.isEmpty(this.get('name')) &&
!Ember.isEmpty(this.get('country'));
}
),
actions: {
save: function() {
if (this.get('isValid')) {
var _this = this;
this.get('model').save().then(function(trip) {
_this.transitionToRoute('trips.show', trip);
});
} else {
this.set('errorMessage', 'You have to fill all the fields');
}
},
cancel: function() {
return true;
}
}
});
controllers/trips/new.js:
import TripsBaseController from './base';
export default TripsBaseController.extend({
actions: {
cancel: function() {
this.transitionToRoute('trips.index');
return false;
}
}
});
routes/trips/new.js:
import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
return this.store.createRecord('trip');
},
actions: {
save: function() {
return true;
},
cancel: function() {
return true;
}
}
});
Any idea how I can solve this? I'm about to try converting it into a View or component but I'm wondering if I can still use {{render}} and I'm missing something simple here.
I'm using ember-cli with ember 1.7.0 and ember-data 1.0.0-beta.10.
I've also added a JS Bin here:
http://jsbin.com/zofive/edit
Here's the answer - I needed to set the model using the Application Route, setupController and controllerFor.
import Ember from 'ember';
export default Ember.Route.extend({
setupController: function() {
this.controllerFor('trips/new').set('model', this.store.createRecord('trip'));
}
});
JS Bin (non-ES6): http://jsbin.com/zofive/13
Thanks to #locks, #abuiles and #xymbol.