2
votes

I have an app that allows management of orders. The default view is a split view with orders on the left and selected order details on the right like so:

  /Orders        /Orders/:order_id
|-----------| |-------------------------------------|
|           | |                                     |
|           | |                                     |
|           | |                                     |
|           | |                                     |
|           | |                                     |
|  List of  | |           Selected Item             |
|   Items   | |              Details                |
|           | |                                     |
|           | |                                     |
|           | |                                     |
|           | |                                     |
|           | |                                     |
|           | |                                     |
|           | |                                     |
|-----------| |-------------------------------------|

I'd like to be able to edit an order in "full-screen" mode so that the URL and template looks like this:

  /Orders/:order_id/edit/
|---------------------------------------------------|
|                                                   |
|                                                   |
|                                                   |
|                                                   |
|                                                   |
|                                                   |
|           Order Edit Interface                    |
|                                                   |
|                                                   |
|                                                   |
|                                                   |
|                                                   |
|                                                   |
|                                                   |
|---------------------------------------------------|

My routes are currently setup as follows:

this.resource('Orders.edit', { path: 'Orders/:order_id/edit' } , function () {
    this.route('customer-details');
    this.route('vendor-details');
    this.route('shipping-details');
}

this.resource('Orders', { path: 'Orders' }, function () {
    this.resource('Order', { path: ':order_id' }, function () {
        this.route('customer-details');
        this.route('vendor-details');
        this.route('shipping-details');
    }
}

And my order routes look like this:

// Orders Route
App.OrdersRoute = Em.Route.extend({
    model: function() {
        return this.store.find('order');
    },

    afterModel: function (orders) {
        this.transitionTo('orders.order', orders.get('firstObject') );
    }
});

// Order Detail
App.OrdersOrderRoute = Em.Route.extend({
    model: function(params) {
        return this.store.find('order', params.order);
    },

    setupController: function (controller, model) {
        controller.set('content', model);
    }
});

// Order Edit Route
App.OrdersEditRoute = Em.Route.extend({
    model: function(params) {
        if (typeof params.order_id !== 'undefined') {
            this.store.find('order', params.order_id).then(function (order) {
                this.controllerFor('orders.edit').set('content', order);
            });
        } else if (typeof params.order !== 'undefined') {
            this.store.find('order', params.order).then(function (order) {
                this.controllerFor('orders.edit').set('content', order);
            });
        }
    },

    afterModel: function(eo) {
        this.transitionTo('orders.edit.customer-details', order);
    }
});

// Order Edit - Customer Details Route
App.OrdersEditCustomerDetailsRoute = Em.Route.extend({
    model: function() {
        try {
            var order = this.get('order');
            return order;

        } catch (e) {
            console.log('ERROR: ' + e);
        }
    },

    beforeModel: function() {
        this.set('order', this.modelFor('orders.edit'));
    },
});

This setup works if I'm in the Orders/:order_id route/template and click the edit button which then sends me to Orders/:order_id/edit with the desired interface and data loaded. But if I try to refresh Orders/:order_id/edit in the browser window nothing loads and I get the following errors. I also don't hit any breakpoints set inside of the Orders/:order_id/edit route when accessing the URL this way.

Uncaught Error: Assertion Failed: Cannot delegate set('classification', N/A) to the 'content' property of object proxy <Synapse.EngineeringOrdersEditDetailsController:ember1242>: its 'content' is undefined. libs.js:2870
    Ember.assert libs.js:2870
    EmberObject.extend.setUnknownProperty libs.js:23933
    set libs.js:9229
    setPath libs.js:9289
    set libs.js:9209
    trySet libs.js:9306
    (anonymous function) libs.js:3416
    tryable libs.js:5964
    tryFinally libs.js:10524
    suspendListener libs.js:5967
    _suspendObserver libs.js:8311
    Binding._sync libs.js:3415
    DeferredActionQueues.invoke libs.js:11346
    DeferredActionQueues.flush libs.js:11398
    Backburner.end libs.js:10861
    Backburner.run libs.js:10916
    apply libs.js:10745
    run libs.js:9378
    runInitialize libs.js:45596
    n.Callbacks.j libs.js:2
    n.Callbacks.k.fireWith libs.js:2
    n.extend.ready libs.js:2
    I libs.js:2

TypeError: undefined is not a function
        at http://localhost:1337/js/app.js:27936:22
        at invokeCallback (http://localhost:1337/js/libs.js:13310:19)
        at publish (http://localhost:1337/js/libs.js:12980:9)
        at publishFulfillment (http://localhost:1337/js/libs.js:13400:7)
        at http://localhost:1337/js/libs.js:18818:9
        at DeferredActionQueues.invoke (http://localhost:1337/js/libs.js:11348:18)
        at Object.DeferredActionQueues.flush (http://localhost:1337/js/libs.js:11398:15)
        at Object.Backburner.end (http://localhost:1337/js/libs.js:10861:27)
        at Object.Backburner.run (http://localhost:1337/js/libs.js:10916:20)
        at executeTimers (http://localhost:1337/js/libs.js:11241:12) libs.js:6663
    logToConsole libs.js:6663
    RSVP.onerrorDefault libs.js:49435
    __exports__.default.trigger libs.js:12274
    Promise._onerror libs.js:12998
    publishRejection libs.js:13405
    (anonymous function) libs.js:18818
    DeferredActionQueues.invoke libs.js:11348
    DeferredActionQueues.flush libs.js:11398
    Backburner.end libs.js:10861
    Backburner.run libs.js:10916
    executeTimers libs.js:11241
    (anonymous function) libs.js:11231

Uncaught Error: Assertion Failed: TypeError: undefined is not a function libs.js:2870
    Ember.assert libs.js:2870
    RSVP.onerrorDefault libs.js:49436
    __exports__.default.trigger libs.js:12274
    Promise._onerror libs.js:12998
    publishRejection libs.js:13405
    (anonymous function) libs.js:18818
    DeferredActionQueues.invoke libs.js:11348
    DeferredActionQueues.flush libs.js:11398
    Backburner.end libs.js:10861
    Backburner.run libs.js:10916
    executeTimers libs.js:11241
    (anonymous function)

I suspect it has something to do with having the Orders/order/edit route outside the hierarchy of the Orders resource but I was unable to get the outlets to play nicely to render the desired interface.

TLDR - How do I get the Orders/:order_id/edit to load the model properly from the URL slug? Using Ember 1.6.1 and Ember-data Fixture Adapter

1
I'm unfamiliar with ember.js but your second route treats :order_id as a parameter whereas your first (seems) to treat it as a literal. - Basic
I should be good with the slug. According to the docs at emberjs.com/guides/routing/defining-your-routes/… the : in :order_id identifies it as a slug. I could be missing something though - ultimatemonty
Can u creating a failing jsbin? Will make solving it a lot more easier - blessenm
I'll see what I can come up with. The issue manifests itself when accessing the URL directly which is hard to replicate with a JSBin. I'll try a transition to the URL and see what happens. - ultimatemonty

1 Answers

0
votes

Two approaches to achieve what you describe are,

  1. Separate resource for editing (basically along the lines if what you've tried)

  2. Nested resource and maintain a property based on which the template will only render the outlet part or not.

Example of these two approaches,

http://emberjs.jsbin.com/wupiwoculoxi/1/edit

hbs

<script type="text/x-handlebars" id="orders">

{{#unless showOnlyEditDetail}}
orders

<br/>
{{#each order in model}}

{{#link-to "order" order.id}}

{{order.name}}
{{/link-to}}
<br/> <br/>

{{/each}}
{{/unless}}

{{!--the following lines before the outlet are shown on purpose for demonstration reasons, the unless helper in this example can hide anything on this template--}}
<hr/>
<i>the value of showOnlyEditDetail:</i><b>{{showOnlyEditDetail}}</b>

<hr/>

{{outlet}}

</script>


<script type="text/x-handlebars" id="order">
the order

<br/>
<br/>
{{this.id}}

<br/>
<br/>

{{this.name}}

<br/>
<br/>
{{#link-to "orderEdit" this.id }}edit1{{/link-to}}
<i>(separate resource)</i>

<br/>
<br/>
{{#link-to "orderEdit2" this.id }}edit2{{/link-to}}
<i>(nested resource and maintaining property)</i>
</script>


<script type="text/x-handlebars" id="orderEdit">
edit the order

<br/>
{{this.id}}
<br/>
{{this.name}}
<br/>
{{#link-to "order" this.id}}back to the order{{/link-to}}
</script>



<script type="text/x-handlebars" id="orderEdit2">
edit2 the order

<br/>
{{this.id}}
<br/>
{{this.name}}
<br/>
{{#link-to "order" this.id}}back to the order{{/link-to}}
</script>

js

App = Ember.Application.create();

App.Router.map(function() {

  this.resource("orderEdit",{path:"orders/:order_id/edit"}, function(){

  });

  this.resource('orders', { path: 'orders' }, function () {

    this.resource('order', { path: ':order_id' }, function () {   
      this.route('customer-details'); 
      this.route('vendor-details'); 
      this.route('shipping-details'); 
    });


    this.resource("orderEdit2",{path:":order_id/edit2"}, function(){});
  });


});

App.IndexRoute = Ember.Route.extend({
  redirect: function() {
    this.transitionTo("orders");
  }
});


var ordersData=[
  {id:1,name:"order1"},
  {id:2,name:"order2"},
  {id:3,name:"order3"}
];


App.OrdersRoute = Ember.Route.extend({
  model: function() {
    return ordersData;
  },
  setupController:function(c,m){
    c.set("showOnlyEditDetail",false);
    this._super(c,m);
  }
});

  /*if the second approach is used then the controller with the specific property 
  (i.e. showOnlyEditDetail) must be defined.*/
App.OrdersController=Em.ArrayController.extend({
  showOnlyEditDetail:false
});


App.OrderRoute = Ember.Route.extend({
  model: function(params) {
    return ordersData.findBy("id",parseInt(params.order_id,10));
  }
});


App.OrderEditRoute = Ember.Route.extend({
  model: function(params) {
    return ordersData.findBy("id",parseInt(params.order_id,10));
  }
});



App.OrderEdit2Route = Ember.Route.extend({
  model: function(params) {
    return ordersData.findBy("id",parseInt(params.order_id,10));
  },
  setupController:function(c,m){
    this._super(c,m);
    this.controllerFor("orders").set("showOnlyEditDetail",true);
  },
  actions:{
    willTransition:function(){
      this.controllerFor("orders").set("showOnlyEditDetail",false);
    }
  }
});