Friday, 8 April 2011

SproutCore Backend Using ASP.NET, MVC3, EF4, Humor Free

These are the key elements to implementing 'step-6' of the SproutCore todo's tutorial for asp.net/mvc.  Use  'Code-First Development with .NET 4 Entity Framework' to create and maintain the database schema.  Let the Entity Framework manage the objects so you don't have to.  To access, the data use ASP.NET MVC3.
Code exerpts follow:

Automatically generates the database schema when class is created:
namespace SproutCoreMVC.Models
{
    public class Task
    {
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int id { get; set; }
        public string description { get; set; }
        public int order { get; set; }
        public bool isDone { get; set; }
        public string guid { get {return "/tasks/" + id;} }
    }

    public class SCModels : DbContext
    {
        public DbSet<Task> Tasks { get; set; }
    }

    public class SCModelsInitializer : DropCreateDatabaseIfModelChanges<SCModels>
    {
        protected override void Seed(SCModels context)
        {
            var tasks = new List<Task>
            {
                new Task {
                    order = 1,
                    isDone = false,
                    description = "Some Description"
                }
};

            tasks.ForEach(d => context.Tasks.Add(d));
            context.SaveChanges();
        }
    }
}
 
This is the REST implementation.  Easy da? 
namespace SproutCoreMVC.Controllers
{
    public class TasksController : Controller
    {
        SCModels db = new SCModels();

        // GET: /tasks        
        [HttpGet]
        [NoCache]
        public ActionResult List()
        {
            return Json(new {content = db.Tasks}, JsonRequestBehavior.AllowGet);
        }
        
        //GET: /tasks/id
        [HttpGet]
        [NoCache]
        public JsonResult Get(int id)
        {
            return Json(db.Tasks.Find(id), JsonRequestBehavior.AllowGet);
        }

        //PUT: /tasks/id
        [HttpPut]
        public ActionResult Update(Task task)
        {
            var taskToUpdate = db.Tasks.Find(task.id);
            UpdateModel(taskToUpdate);
            db.SaveChanges();
            return Json(taskToUpdate);
        }
        
        //POST: /tasks
        [HttpPost]
        public ActionResult Create(Task task)
        {
            db.Tasks.Add(task);
            db.SaveChanges();

            //set location and to redirect headers
            Response.StatusCode = 201;
            Response.RedirectLocation = task.guid;
            return new EmptyResult();
        }

        //DELETE: /tasks/id
        [HttpDelete]
        public ActionResult Delete(int id)
        {
            db.Tasks.Remove(db.Tasks.Find(id));
            db.SaveChanges();
            return new EmptyResult();
        }
    }
} 
I left out the routing information. As you can see the integration between SproutCore and asp.net MVC3 and Entity Framework 4 is quite straightforward and painless. I actually only write about 70 lines of code.  Is much easier than you half of project.

SproutCore TabView ; A View from the trenches, now (mostly) rhetoric free.

Beautiful Anastasia Volochkova

Anastasia Volochkova is much like SproutCore's TabView;  She is a beauty to behold, but do not cross her or she will sue (and win).


SproutCore's SC.TabView is much the same.  You must do things TabView's way, and if you want to do something else then you will have to build it.

TabView will accept a few parameters:
items (or itemsBinding) - an array of items 
nowShowing - the view that will be loaded by default,
itemTitleKey - button label
itemValueKey - the value that the button will hold
You can also pass itemActionKey, and itemTargetKey, but they don't work as I would expect (or want).
 .... more on this later

Under the hood, TabView rely's on SegmentedView, for buttons and a  ContainerView to hold the view elements.  The TabView creates a SegmentView for each item in the passed in 'items' array.  Items can either be pre-defined or else bound as itemsBinding. 

Where things go 'funny' is when you specify the itemActionKey and/or itemTargetKey;  With those specified, the TabView's buttons fire off the action, but the 'value' component is somehow lost in translation and no SegmentView change is displayed.  Perhaps I don't know what I'm doing, but that's my experience.  Also, the SegmentView button is not put into the 'on' state.

I'll illustrate what I mean with a few screenshots:

First a simple TabView rendering:

mainView : SC.TabView.design({
    layout : {top : 36, left : 40, right : 40, bottom : 40},
    nowShowing : 'otherStepsView',
    itemTitleKey : 'title',
    itemValueKey : 'value',
    itemIconKey : 'icon',
    itemActionKey : 'action',
    itemTargetKey : 'target',
    items : [ {
      title : 'Steps',
      value : 'stepsView',
      icon : 'sc-icon-folder-16'
    },
    { title: 'otherSteps',
      value: 'otherStepsView',
      icon: 'sc-icon-folder-16',
      action: 'someAction',
      target: 'MyApp.aController'
    }
    ]
  })

to create....
Looks good right?

Exactly as expected.  Lets click on the "Steps" tab.

Still exactly right.

Lets click on the 'otherSteps' tab.
So, basically this sucks.  We should be able to work around this by setting the 'nowShowing' property in the 'MyApp.aController.someAction' function.

someAction: function(something){
  console.log('You clicked me! I am: '+something);
  console.log('You expect me to have a value of "otherStepsView" but surprise: I am: '
    +something.get('value'));
  var theOtherView = MyApp.mainPage.get('otherStepsView');
  MyApp.mainPage.mainPane.mainView.set('nowShowing', theOtherView);
  return true;
}

which yields:
The right view is shown, but the 'otherSteps' button isn't highlighted.  *sadface*

Sadly, I can't think of a way to easily fix this.  Perhaps its expected behavior.

Code available on github:  git://github.com/MikailCliftov/SC.TabView-Woes.git