Thursday, May 26, 2016

An Angular 2 Master/Detail using JavaScript

In this post, I'll talk about a complete Master/Detail sample I created for Angular 2 using JavaScript.

UPDATE 7/22/16: Fixed breaking changes in Angular with file refs.

[VIEW THE Plunker]


Why a JavaScript Sample?

It is apparent that TypeScript is the language of choice for Angular 2, and with good reason. But when teaching Angular 2 concepts to someone for the first time, it can be a benefit to stick with JavaScript, thereby removing the confusion of new TypeScript constructs. Not surprisingly, there is a dearth of full examples using JavaScript in Angular 2, so maybe this sample can help fill that void.

Using the Sample with MVC Web API

This sample was originally created with the Service hitting a Web API, but was adjusted to use an in-memory array for Plunker. The following tweaks are needed for hitting a Web API:

In beers.service.js, change the CRUD methods to use appropriate ng.http methods, and point to the MVC Web API url. Note how we add the ng.http.Headers collection as a parameter to the post call. This is important to avoid an error "415 / Unsupported Media Type: No MediaTypeFormatter is available to read an object of type 'xxx' from content with media type 'text/plain'."


        getBeers: function () {
             return this.http.get("api/beers").map(
                 function (res) {
                     res = res.json();
                     return res;
                 }
             );
         },

         saveBeer: function (beer) {
             var headers = new ng.http.Headers();
             headers.append('Content-Type', 'application/json');
             return this.http.post("api/beers", JSON.stringify(beer), { headers: headers });
         },

         deleteBeer: function (beer) {
             return this.http.delete("api/beers/" + beer.BeerId);


         }

In beer-master.component and beer-detail.component, the promise will now be an observable, so we instead use subscribe to await the return:

             this._beersService.getBeers().subscribe(
                 function (data) {
                     self.beers = data;
                 },
                 function (err) {
                     self.error = err;
                 }

             )

The Web API Controller itself should look something like the following.

    public class BeersController : ApiController
    {

        static Dictionary<int, Beer> beers = new Dictionary<int, Beer>()
          {
            { 1, new Beer { BeerId = 1, BeerName = "X Double Indian Pale Ale", BreweryId = 1, AlcoholPercent = 10.0M, ImageUrl = "http://middleagesbrewing.com/sites/default/files/styles/beer_label/public/labels/Double%20IPA%20Tap%20Marker.png?itok=agxkSzzn" }}

            // ... add more beers here :)

        };

        public IEnumerable<Beer> GetAllBeers()
        {
            return beers.Values.OrderBy(b => b.BeerId).ToList();
        }

        public IHttpActionResult GetBeer(int id)
        {
            var beer = beers.FirstOrDefault((p) => p.Key == id);
            if (beer.Value == null)
            {
                return NotFound();
            }
            return Ok(beer);
        }

        // POST api/values
        [HttpPost]
        public IHttpActionResult Post(Beer beer)
        {
            if (beer.BeerId == 0)
            {
                // new beer
                beer.BeerId = beers.Max(b => b.Value.BeerId) + 1;
                beers.Add(beer.BeerId, beer);
            }
            else
            {
                var beerEdit = beers.Where(b => b.Key == beer.BeerId).First().Value;
                beerEdit.BeerName = beer.BeerName;
                beerEdit.ImageUrl = beer.ImageUrl;
                beerEdit.AlcoholPercent = beer.AlcoholPercent;
            }
            return Ok(beer);
        }

        // DELETE api/values/5
        [HttpDelete]
        public IHttpActionResult Delete(int id)
        {
            beers.Remove(id);
            return Ok(id);
        }

    }

No comments:

Post a Comment