have bootstrap alert messages as an ember component (they display success error warning messages...). How could I insert a message on an event? Lets say form validation is not successful, I want to pass a message with the folowing component:
{{alert-mes message="The passwords must match" type="error"}}
Embner has two way binding, so you don't need to pass a different message everytime, instead you can have an attribute that changes in the controller, and have your component listen to changes:
// controller.js
showModal: false,
message: null,
actions: {
somethingHapened: function() {
this.set('message', 'The passwords must match');
this.set('showModal', true);
}
}
// template
{{alert-mes message=message type="error" show=showModal}}
// component
onShowModal: function() {
if (this.get('show')) {
// display the modal somehow
// this.$().show(this.get('message'));
} else {
// hide the modal
}
}.observes('show')
Related
I have installed the ember-g-recaptcha addon in my ember app using ember install ember-g-recaptcha. I'm able to see the recaptcha in the UI. But how do I check whether that box is checked or not. If it's not checked my form should not submit and should display an error message.
I want to display this error message when the response is empty. I have written this in my controllers:
onCaptchaExpired() {
$("#message").show();
$("#message").css("color", "red");
$("#message").html("Recaptcha response cannot be empty.");
},
First you really should rely on ember to manage the DOM, and don't do it manually with jQuery.
A simple solution could be to just toggle a boolean when the g-recaptcha component fires the actions:
template:
{{g-recaptcha onSuccess=(action "onCaptchaResolved") onExpired=(action "onCaptchaExpired")}}
{{captchaError}}
<button disabled={{isInvalid}} />
component:
Ember.Component.extend({
isInvalid: true,
captchaError: null,
actions: {
onCaptchaResolved(captchaResult) {
this.set('isInvalid', false);
},
onExpired() {
this.set('isInvalid', true);
this.set('captchaError', 'Recaptcha response cannot be empty.')
}
}
})
This should give you an idea how you could do this.
I have a component in my application.It have a form with text fields.It will have a submit button.When submit is pressed it will send a post request to the server.I also handled a keyboard event in components js file.When enter is pressed it will send a post request to the server.When the enter key is pressed two times continuously it is making two post request to the server with first request success and second request failed.
I want to make my app in such away even if the user presses the enter key two times continuously it should send only one post request to the server.Can any one help me solve this issue.Thanks in advance.
components js file:
export default Component.extend({
keyDown:function(event){
let self = this;
if(event.keyCode === 13){
self.send('submitform');
return false;
}
actions: {
submitform(){
//logic to handle the post request to the server
}
}
Try usig Ember.run.debounce,
export default Ember.Component.extend({
keyDown: function(event) {
let self = this;
if (event.keyCode === 13) {
// self.send('submitform');
Ember.run.debounce(self,self.get('submitform'),400);
return false;
}
},
submitform(){
//handle submit form logic
}
});
You can play with twiddle here
You will want to disable submitForm() until your POST request is complete. You can do this by setting a property submitting on the component and turning it on before the POST and off once the promise is resolved. Perhaps try something like:
export default Ember.Component.extend({
submitting: false,
keyDown: function(event) {
if (event.keyCode === 13) {
this.submitform();
}
},
submitform() {
// Run only if not currently submitting
if (!this.get('submitting')) {
// What to do when submit succeeds
const success = () => {
this.set('submitting', false);
}
// What to do when submit fails
const fail = () => {
this.set('submitting', false);
}
// Do the POST request
this.set('submitting', true);
this.get('someModel').save().then(success).catch(fail);
};
}
});
And, unrelated, this allows you to do fun things with your template such as disabling and styling the submit button for as long as the POST promise is not resolved yet:
<button {{action 'submitForm'}} disabled={{submitting}} class="{{if submitting 'loading'}}">
{{#if submitting}}
Submitting ...
{{else}}
Submit
{{/if}}
</button>
Oh and lastly, no need to use let self = this; anymore. Use ES6 arrow functions () => { ... } instead so you can keep using this inside.
I have a component which is my main interface. Inside this component, clicking a button opens ionic 2 modal which allows to choose items.
My modal page (itemsPage):
..list of items here
<button ion-button [disabled]="!MY_TURN || !selectedItem || !selectedItem.quantity"
(click)="useItem(selectedItem)">
<span>Choose item {{selectedItem?.name}}</span>
</button>
useItem() should:
Send item data to my main interface component
Close the modal
Execute a method in my main interface
How I can perform such actions? Couldn't find any documentation about communicating between modal and component in Ionic 2.
It is simply a matter of using parameters in viewController.
In your main interface component,
let chooseModal = this.modalCtrl.create(itemsPage);
chooseModal.onDidDismiss(data => {
console.log(data);
});
chooseModal.present();
In your modal page,
useItem(item) {
this.viewCtrl.dismiss(item);
}
Modal Controller link here
This is a clear example of getting data from modals in ionic.
You need to add a handler for modal’s onDismiss() and then return the data from the modal itself by passing the data to the ViewController’s dismiss() method:
// myPage.ts
// Passing data to the modal:
let modal = Modal.create(myModal, { data: [...] });
// Getting data from the modal:
modal.onDismiss(data => {
console.log('MODAL DATA', data);
});
this.nav.present(modal);
on the modal page
// myModal.ts
constructor(private navParams: NavParams, private viewCtrl: ViewController) {
// Getting data from the page:
var dataFromPage = navParams.get('data');
}
dismiss() {
// Returning data from the modal:
this.viewCtrl.dismiss(
// Whatever should be returned, e.g. a variable name:
// { name : this.name }
);
}
What I want: an error handling that handles different http errors (401, 404, 500) globally. It shouldn't matter where or when an http error occurs.
So far I implemented an error action in the application route that will be called on any adapter errors on route's model hook. That's working fine.
What is not covered is the case when I work with records in other contexts like record.save(). There I need to separately handle the error on the promise.
Moreover I don't only want to have a default error handler but more like a fallback.
Ok, before talking too much let's have an example implementation of my use cases.
application route
The application error action should be the default / fallback error handler.
actions: {
error: function(error) {
var couldHandleError = false;
if (error.errors) {
switch (error.errors[0].status) {
case '401':
// User couldn't get authenticated.
// Redirect handling to login.
couldHandleError = true;
break;
case '404':
case '500':
// Something went unexpectedly wrong.
// Let's show the user a message
couldHandleError = true;
break;
}
}
// return true if none of the status code was matching
return !couldHandleError;
}
}
Some route
In this case the application error action is called.
model: function() {
return this.store.findAll('post');
}
Some controller
In this case the application error action is NOT called.
(I know, the following code probably doesn't make sense, but it is just supposed to illustrate my requirements)
this.store.findRecord('post', 123);
Some other controller
In this example the application error action is not called, for sure, since I use my own handler here (catch()).
But as you can see in the comments I do want to use the default handler for all status codes other than 404.
this.store.findRecord('post', 123).catch(function(reason) {
if (reason.errors[0].status === '404') {
// Do some specific error handling for 404
} else {
// At this point I want to call the default error handler
}
});
So is there a clean and approved way of achieving that? I hope I could make my problem clear to you.
I think I have my final solution I want to share with you. Basically I took the ideas of the guys commenting my question and extended them so they fit my needs.
First I created a mixin with the main logic. Since I want it to be as generic as possible, it distinguishes between a) controller / route and b) jquery / adapter error. So it doesn't matter from where you call it and whether your error object is originally from an jquery Ajax request or an ember adapter.
import Ember from 'ember';
export default Ember.Mixin.create({
ajaxError: function(error) {
if (!error) {
Ember.Logger.warn('No (valid) error object provided! ajaxError function must be called with the error object as its argument.');
return;
}
// Depending whether the mixin is used in controller or route
// we need to use different methods.
var transitionFunc = this.transitionToRoute || this.transitionTo,
couldHandleError = false;
switch (this._getStatusCode(error)) {
case 401:
transitionFunc.call(this, 'auth.logout');
couldHandleError = true;
break;
case 404:
case 500:
// Here we trigger a service to show an server error message.
// This is just an example and currently not the final implementation.
// this.get('notificationService').show();
couldHandleError = true;
break;
}
// For all other errors just log them.
if (!couldHandleError) {
Ember.Logger.error(error);
}
},
_getStatusCode: function(error) {
// First check for jQuery error object
var status = error.status;
// Check for ember adapter error object if it's not a jquery error
if (!status && error.errors && error.errors[0].status) {
status = parseInt(error.errors[0].status);
}
return status;
},
});
Next I reopened some Classes (inside app.js) to make this functionality globally available:
import AjaxErrorMixin from 'app/mixins/ajax-error';
Ember.Route.reopen(AjaxErrorMixin);
Ember.Controller.reopen(AjaxErrorMixin);
Ember.Component.reopen({
_actions: {
// Passing ajaxError per default
ajaxError: function(error) {
this.sendAction('ajaxError', error);
}
}
});
Finally I added some actions to the application route:
actions: {
error: function(error) {
this.send('ajaxError', error);
},
ajaxError: function(error) {
this.ajaxError(error);
},
}
Why do I have two actions doing the same stuff? Well, the error action is called on errors on route's model hook. I could stay with that action, but in the rest of the application where I explicitly call this action I want a more meaningful name. Therefore I also created a ajaxError action. You could stay with one action, for sure.
Now you can use this everywhere:
Route / Controller:
this.ajaxError(error);
Component:
this.sendAction('ajaxError', error);
For sure, you also need to pass the action out of the component to be handled by the application route:
{{some-component ajaxError="ajaxError"}}
This works for nested components, too. You don't need to explicitly send this action further inside the component.js file since we reopened the Component and passt this action into.
I hope I can help other people with that implementation. Also any feedback is welcome.
You can try to do something with these events (put these lines in app.js before app initialization):
Ember.onerror = function (error) {
console.log('Ember.onerror handler', error.message);
};
Ember.RSVP.on('error', function (error) {
console.log('Ember.RSVP error handler', error);
});
Ember.Logger.error = function (message, cause, stack) {
console.log('Ember.Logger.error handler', message, cause, stack);
};
I learned about them from https://raygun.io/blog/2015/01/javascript-error-handling-ember-js/, you may find some details there.
I use the RESTadpater to persist data. When a validation error occurs, I want to return a 422 response and then log the errors and show an indication next to each incorrect field.
My REST response status code is as follows:
Status Code:422 Unprocessable Entity
My REST response body is as follows:
{
"message": "Validation failed",
"errors": [
{
"name": "duplicate"
}
]
}
In my controller, the becameInvalid fires correctly.
App.AuthorsNewController = Ember.ObjectController.extend({
startEditing: function () {
//Create a new record on a local transaction
this.transaction = this.get('store').transaction();
this.set('model', this.transaction.createRecord(App.Author, {}));
},
save: function (author) {
//Local commit - author record goes in Flight state
author.get('transaction').commit();
//If response is success: didCreate fires
//Transition to edit of the new record
author.one('didCreate', this, function () {
this.transitionToRoute('author.edit', author);
});
//If response is 422 (validation problem at server side): becameError fires
author.one('becameInvalid', this, function () {
console.log "Validation problem"
});
}
...
2 QUESTIONS:
I want to log below the 'console.log "Validation problem"', the complete list of errors returned by the server. How can I do that ?
In my hbs template, I want to indicate an error next to the relevant field. How can I do this ?
I am not sure that the data returned via REST adapter is correct. So problem might be at the REST side or at the Ember side ...
Solution:
In controller save function:
author.one('becameInvalid', this, function () {
console.log "Validation problem"
this.set('errors', this.get('content.errors'));
});
In hbs template:
{{view Ember.TextField valueBinding='name'}}
{{#if errors.name}}{{errors.name}}{{/if}}
Here is how I do it, may not be the best practice but it works for me:
instead of using commit(), I use save(), and if you wonder what's the difference, here is the link. I haven't tried your approach of using transaction, but basically I create the record using record = App.Model.createRecord(...), and here is the code of my apiAddShop function inside the AddShopController:
apiAddShop: function() {
//console.log("add shop");
newShop = App.Shop.createRecord({name:this.get('name'), currentUserRole:"owner"});
//this.get('store').commit(); // Use record.save() instead, then() is not defined for commit()
var self = this;
newShop.save().then(function(response){
self.transitionToRoute('shops');
}, function(response){
// if there is error:
// server should respond with JSON that has a root "errors"
// and with status code: 422
// otherwise the response could not be parsed.
var errors = response.errors;
for(var attr in errors){
if (self.hasOwnProperty(attr)) {
self.set(attr+"Error", true);
self.set(attr+"Message", Ember.String.classify(attr)+" "+errors[attr]);
}
console.log(attr + ': ' + errors[attr]);
}
console.log(response.errors.name[0]);
});
},
the above code assume there is a attrError(boolean) and attrMessage(string) for each of the attributes in your form. Then in your template, you could bind the class of your field to these error attributes, such as <div {{bindAttr class=":control-group nameError:error:"}}>, and the error message could be easily display next to the form field such as: <span {{bindAttr class=":help-inline nameError::hidden"}} id="new_shop_error">{{nameMessage}}</span> Here is my example handlebar gist (have to use gist here, since SO is escaping my html inputs).