Component-Route Two way Communication


#1

I have implemented register as component which handles Client side validation as well as other issues. When validation is ok I send registration data to route via sendAction. The problem is, at the server some field might be invalid. For example username must be unique and current user might well use the user that is already in the database. So I need a way to tell the component that there was error in server.

As new ember.js programmer trying to avoid the Controller as much as I can, what is the way to go?

TIA, Stefano


#2

You could have a property on your model called serverErrors that would inform the component that the model wasn’t successfully saved.

When saving the model:

let model = this.get('model');
model.save().then(
  () => { model.set('serverErrors', nil); }, 
  (errors) => { model.set('serverErrors', errors); }
);

Then in your component, you could check for server errors and display a message.

{{#if model.serverErrors }}
  <p class="text-warning">Your model couldn't be saved due to {{ model.displayErrors }}</p>
{{/if}}

There’s probably a bunch of different ways to solve this based on your design. This was the first that popped into my head.


#3

@palmergs can you give a simple example? I have this scenario and I have no idea how to do it

  1. Component renders a form
  2. Submitting the form invokes router signup method
  3. Signup creates user model and tries to save on server
  4. Server returns validation errors
  5. Router sends back the message via template to component

No I can do 1-4 and cannot figure out the 5. I’m trying to make sense of what you wrote but in its current form I cannot. Best regards, S.


#4

I’m having trouble translating step 5 into thing that ember would support.

What if, in step 1, that component was instantiated with an “empty” non-persisted user model; one that you could keep track of through the entire existence of your sign up form? In other words, the sign up form continues to exist until the user model is persisted and free of errors.

{{my-signup-form model=model action="signupUser"}}

In the component template it’s something like:

{{#if model.isNew}}
  {{#if model.serverErrors}}
    <p>Couldn't save the user due to {{ model.serverErrors }}</p>
  {{/if}}

  ... all the rest of the form stuff ....
{{/if}}

When first displayed the User model is empty and has no errors so you just see the form. Once the form is submitted you try to persist it in the signupUser action. If the model saves, then the component hides itself. If the model doesn’t save then the model gets the errors from the server and, due to the binding of the model to the component, the user immediately sees the error message.

actions: {
  signupUser: function(userModel) {
    userModel.save().then(() => { 
      userModel.set('serverErrors', null);
      // userModel.get('isNew') is now false and errors are empty; the form disappears...
      }, (errs) => {
        userModel.set('serverErrors', errs); 
        // at this point, because the signup form component is bound to the user model
        // the error message displays. 
      });
  }
}

It’s not “sending” a message as you describe in step 5. It’s more of monitoring the state of the model until the model is good.

Hope this helps.


#5

Thanks for great example. I now understand better. I will use this knowledge to fix my issue.

As for 5 its my poor language. Forgive me for that. Am not well versed in ember terms yet. What I mean is once I have errors from server I want to display them back to the user, that simple!

One more question, the signupUser action takes a model from component, does it mean its created inside component and keep circulating? I’m sorry am not good at graphically explains things (forgive me for that too), but that sound too complicated for such a task. I just want to have a way to tell the component that “Hey display for me this”. As for hiding the form, I plan to do transition to index with message of success. So the form will stay there until user either closes browser or succesful registers.

I hope I have not added confusion.

TL;DR I want to post just error object from Route to Component

Thanks for your time!


#6

So I ended up adding additional attribute and used it to modify the model. Here is sample code. I’m not sure if its the best but so far it works

import Ember from 'ember';

export default Ember.Route.extend({
    _model:{},
    model(){
        return this.get("_model");
    },
    actions:{
        signUp(userInfo){   
            this.user = this.store.createRecord('user', userInfo);
            let route = this;            
            this.user.save().then(function(){ //success 
                alert("Aw! resgistered well!");
            }, function(error){ //error    
                route.set("_model.errors",  error.errors); 
                
            }).catch(function(err) {
                console.log(err);
            });  
        }
    },  
     
});

#7

@mtangoo this post may provide some more insight into the default error behavior in ember routes and also shows an example of custom error motivations - https://pixelhandler.com/posts/emberjs-handling-failure-using-route-error-substates