Jump-Start Single Page Apps | John Papa

John Papa

Evangelist on the loose

Jump-Start Single Page Apps

...

Ready to learn how to build a Single Page App from scratch? This new course provides a beginner’s jump-start to getting off and running with a SPA quickly. You’ll have all the fundamentals in place and get to join me in walking through building all of the code.

spajs

This SPA jump-start course fits in nicely to ease you into Single Page App development and get you moving quickly and efficiently. This is an end to end course that starts from File | New project and builds a fully functional SPA with multiple pages, insert, update, delete, validation, sharing data, across views, modularity, AMD, view compositions, and much more. It begins with an exploration of the new ASP.NET SPA templates including Hot Towel. In fact, the Hot Towel template was inspired and created specifically for this course.

The intermediate SPA course covers more topics, how to build more of the components manually, discusses many of the choices you can make, and covers some scenarios in more depth such as HTML5 Boilerplate, Web Optimization, Web API design, building rich data capabilities manually, and Responsive Web Design (and more).

To summarize:
SPA JumpStart for beginners –> Teach me the core and “how”
SPA Intermediate –> Teach me more depth, more topics, and more “why”

Course Description

Build a Single Page Application (SPA), in JavaScript and HTML, with a rich user experience and runs on almost any device! Start from File | New project and build a fully functional SPA with multiple pages, insert, update, delete, validation, and more. We’ll explore and build with the ASP.NET Hot Towel SPA template as well as powerful JavaScript libraries such as Durandal, Breeze, Knockout, toastr, Twitter Bootstrap, and jQuery.

Live Demo

I published a live demo of the Code Camper Jump-Start SPA online, hosted on Windows Azure. Please check it out give it a whirl.

Source Code

The course is designed so that you can follow along. However, if you want the source code it is included with the Pluralsight Plus subscription. In fact, each module builds off each other and I provided a before and after solution for every module. The final code base is in the “after” solution of module 10.

Topics

  • Introduction to SPA
  • SPA Templates
  • SPA from Scratch
  • Foundations and Adding a View
  • Navigation with Durandal
  • Data Management with Breeze
  • Getting Data Efficiently
  • Saving and Checking for Changes
  • Adding and Deleting Data
  • Validating Data

What’s Next?

I have a bunch of courses in mind including expanding on Durandal, Breeze, Knockout, and JavaScript in general. I also have a ton of SPA tips that I have gathered and will produce a tips course. I expect to get moving on one of those soon or some other ideas I have once I take a few days off from authoring and unwind :)

Questions?

I recently posted a list of SPA questions I often receive. Please check this out as it likely contains some of the questions you may formulate at some point. If not, please feel free to ping me either by:

tags: course javascript pluralsight SPA
  • Kevin

    What is the easiest way to load the VSIX extension for templates into VS2012? When I open VSIX file in VS I am getting binary data. Template is not accessible from gallery browser within Extensions manager.

    • Tiago Reis

      Kevin, you have to install it just like an msi. It’s outside Visual Studio that you install it. After that, just open Visual Studio –> New Project –> ASP.NET MVC 4 Web Application (for example ) –> HotTowel Single Page Application should be available for you to choose.

  • Sinan

    Hi John,
    regarding What’s Next?, i hope you can provide us with SPA LOB application like customers,orders,order_details
    this type of application had many Challenges like one to many with same view order and order_details

  • Sam

    Great course. On top of learning how to use the hot towel framework works, it’s a good review on knockout.js and an excellent course on learning how to organise javascript code (raviolis vs spaguettis). I understood at last how and why to uses promises.
    Keep up the great work :)

  • Sascha

    Well done, watching the course right now.

    I have built a SPA based on the older tutorial and i was doing my routing with sammy and a publish/subscribe mechanism because i needed some kind of sub-navigation.
    I want to use the new framework for my upcoming page.
    Do you have an idea on how to do a navigation hirachy?
    Meaning for example the admin page with sub pages. So the admin page has to activate as the shell below the main-page shell with its own navigation of the admin pages.
    Is this possible with durandal?

    Keep up the good work!
    Greets

    • John

      Sascha – First you have to decide how the UI will look and work. Let’s assume you want a tabbed main menu and then a subset of tabs for this sub view. You would need to create the sub set of routes and make them visible:false. perhaps add a property to the settings parameter of the routes to indicate that these are a specific set of menu items … settings: {submenu: ‘cusomerdetails’}. Then you can write code to find all of the customer details sub menu items in your viewmodel, and bind them. This is similar to what I do with adminRoutes in the course.

  • Luciano

    John,

    Can you show in your course Authentication? Whats the best way to control login and roles with SPA

    • John

      Luciano – The ASP.NET SPA Template released in Feb has auth built into it. Look at their example and you can apply it in Hot towel.

  • Kent

    John,
    Thanks for the Pluralsight courses (I’ve already watched your 1st one a bunch of time and just started watching this latest one). Very helpful.

    In the first part of your new Jump-Start SPA course you mention a site – trello.com. I’ve looked at that site and it seems very interesting too. In your SPAs you tend to navigate to different views, whereas trello seems to stay on one page and the end-user adds lists and cards to the lists all on-the-fly.

    If you were sitting down right now to develop a SPA similar to trello, how would you go about doing it and would you use the same 3rd party libraries? That is, how would you develop a SPA in which the end-user can add/edit/delete the UI and UI components on the fly?

    Thanks!

    • John

      Kent – I had not thought about that much, but I like how Trello does it. I do not like the modal dialog for editing an entry, though. For that I would prefer a UX where the screen changes to the editable view, then back to the main dashboard. They have a neat dashboard but there is no reason to always show it. It could be done with the Hot Towel stack. Might be a fun project for me :)

  • http://www.GreenstoneStrategic.com Chris G

    Hi John.Am absolutely loving your Single Page Apps Jumpstart course! I’ve been diligently following along in VS2012 and build/run successfully until I get to the 4th module, Foundations and Adding a View, Coding the Shell with Durandal.js at 14:14, where you indicate that it’s time to build, run and view our work thus far. When I do so, the splash screen appears, but stops. Checking out the console tab in Chrome developer tools indicates that we got past “Debug Mode Enabled”, “Starting Application” and “Started Application”. However, after that it displays “Uncaught TypeError: Cannot read property ‘nodeType’ of null”, pointing to Knockout-2.2.1.debug.js, line 1724. A Google search indicates that I may be trying to bind an HTML element before it was created. I’ve gone through the module again, as well as compared my code to your “after” version of the code. This is, no doubt, an issue caused by me, but I can’t seem to find the issue. Might you have any clues as to what I might have left out or misplaced?

    Thanks

    • John

      CHris – Thanks, I appreciate that.

      If you can shoot me a link to the code I’ll take a quick look. Send me an email from my contact page with a link to the code in a skydrive or dropbox folder.

      • Carl

        One step at a time… :-)

        I now have the same problem as Chris above. Please would you let me know if you found out what was causing this error?

        • Carl

          Argh. Ignore me. I failed to include the “applicationHost” div! All good now, moving on with the course, thankyou!

  • http://www.GreenstoneStrategic.com Chris G

    Hi John,
    A lot can be said for a good meal and time away from the code to give one clarity. Indeed, I was the culprit, typing “applicationhost”, instead of “applicationHost” in index.cshtml. This magically resolved the Knockout issue. Thank you in advance for any help you would have surely offered. Being an “old” WPF/MVVM guy, I think what you’ve done here with SPAs is to point the way to the next step in software development, gently providing a bridge. Many thanks for all your continued efforts.

  • Carl

    Hi John,

    Hugely enjoying the course but I’m having a fundamental problem getting the web api to work. Have since tried about 3 different projects both in vb and c# and can’t get it to work at all. I’m probably missing a crucial step.

    I start with a blank web application, install web api nuget package, add a “global.asax” file and a “testController” then try and launch the site and go to “http://localhost:16489/api/test” but I just get “404: Not found” every time.

    It hasn’t added a webapiconfig file to my app_start folder though – maybe that’s the problem? If I use yours it still doesn’t work. I think I’m missing a crucial step – anyone any ideas please?

    • Carl

      hmm. I suspect I have a problem with the razor part of the project maybe. I had this when testing hot towel out. As I use vb and not c# I had to translate all the code. This led to the page not working and I never worked it out. Same thing is happening in the SPA jumpstart project now. I’ve converted all the code yet when I look at the network tab in chrome I have:
      GET http://localhost:57219/scripts/modernizr 404 (Not Found) index.vbhtml:18

      GET http://localhost:57219/scripts/vendor 404 (Not Found) index.vbhtml:18

      GET http://localhost:57219/Content/css 404 (Not Found) index.vbhtml:8

      Uncaught ReferenceError: $ is not defined (in durandal ciewEngine.js file)

      I suspect that something isn’t configured right for me to be getting those errors.

      • Carl

        This is my index.vbhtml file:

        @imports System.Web
        @imports System.Web.Optimization

        CCJS

        @Styles.Render(“~/Content/css”)

        // Must be first. IE10 mobile viewport fix
        if (navigator.userAgent.match(/IEMobile\/10\.0/)) {
        var msViewportStyle = document.createElement(“style”);
        var mq = “@@-ms-viewport{width:auto!important}”;
        msViewportStyle.appendChild(document.createTextNode(mq));
        document.getElementsByTagName(“head”)[0].appendChild(msViewportStyle);
        }

        @Scripts.Render(“~/scripts/modernizr”)

        @Scripts.Render(“~/scripts/vendor”)

    • John

      Carl – There are several ASP.NET packages you need to get some of this working. I suggest you start with HotTowel NuGet or template. Or, if you want to build on your own, install all of the packages I install in my SPA JumpStart course. (Some packages install others too, so its not always obvious). This is one reason why I created Hot Towel

      • Carl

        Thanks John

        I mentioned above – I did start with Hot Towel some weeks ago. I can’t get it to work in vb. C# no problem but I can’t do C#! I’m very good at using translation services, get clean compiles, but the script rendering stuff always fails (leaving me with lines like which don’t exist)

        I then tried starting simple web apps and just adding the web api to them. I have since discovered that I can add the App_Start folder, webapiconfig and then modify global.asax to get them to work.

        I’ve gone through your jumpstart app with your instructions step by step to get to the bit where you test the speakers controller. Mine fails with 404 not found and I can’t find out why!

        • John

          Carl – Let’s move this to StackOverflow and link to the issue. First thing I would do is compare the package.config files to make sure all the NuGet’s were installed.

        • Mark Krieger

          Carl/John,

          I have, rather unsuccessful, tried to convert this project to VB. I’ve been tearing out my hair and since Carl has had some success, I wonder if you’d be willing to share the skeleton with me so I can get a jumpstart on things. I’m mark at markkrieger dot com. I’d sure appreciate it.

          -Mark

          • Carl

            I don’t have a problem with doing that but I’ll wait for the OK from John as I don’t want to unwittingly hand over material that may be copyrighted!

            What say you John?

  • Paul

    Howdy John, I finished watching your new course over the weekend, I’m blown away, truly great stuff. In a LOB application one would have way too much breeze code in a single datacontext.js, I’m thinking about using datacontext to maintain state of the breeze manager etc. with perhaps some error handling and notifications, and separating the rest into a ‘dataservice per module’ i.e. customer-dataservice.js, orders-dataservice.js and so on, each dataservice would ‘require’ datacontex. Just wonder what you think about this type of approach?

    Look forward to more amazing SPA courses.

    Paul
    (just wish exercise files where available to us non PLUS subscribers)

    • John

      Paul,

      Thanks for watching! The breeze site has a docs section and there are some samples on how you can create multiple managers to achieve different patterns. For example you can have different units of work in the client with different managers, thus saving one view and not another, if you prefer. You could create one datacontext, as I did, with one manager. Or another options is to create multiple datacontext modules that all share the same manager. I think this is what you are interested in, just to break the code out a bit.

    • Greg

      We are implementing a similar design whereby our view models require a data context and a repo for soc. To avoid conflicts resulting from multiple entity managers when creating entities our vm’s pass the data context to the repo (what you call data service). Good Luck!

      Can’t wait for the tips!

  • Carl

    Sorry John! One more niggling error I just can’t lose now. I have almost all of section 4 complete now I’ve resolved my earlier problem and even though I’ve now replaced virtually every one of my self-written files with your files from “after” I still get:

    Uncaught ReferenceError: ko is not defined (line 284 of composition.js in durandal)

    …when I start up the app. I’ve removed and re-added the durandal package. No change.

    Not many hits on google for this… anyone got any idea where I could start looking please. So close to getting main bit working!

    • Carl

      Solved it. During the copying around of files I managed to include an early version of my bundleconfig without the knockout link! Amazing what a night’s sleep can do to make the brain work properly…

    • John

      Carl – That usually means Knockout is not in the bundle, or is in the wrong order, or is misspelled in the bundle.

  • http://ntrmetals.com Paul

    I am sure you have said this before but I cannot seem to find the post that discusses it. I noticed that you get a lot of intellisense for your js, even items loaded with RequireJS. I am not getting that and it certainly makes new (new to me anyhow) libraries less discoverable. Can you cover what is needed to get VS2012 to surface the rich intellisense I see in your videos and were I can get some of this magic?

    • John

      Paul

      You should be able to use the triple slash syntax to get intellisense in Visual Studio 2012 like this /// . However, at times this doesn’t work for me and I then use Resharper (a paid plug in) which provides it for me. I am hoping that the intellisense issues are worked out soon … or I am doing it wrong. My issues stem from using AMD modules (require.js)

      • http://ntrmetals.com Paul

        Thanks for the Rapid reply. I thought I saw Resharper icons, so that was a helpful clarification. I too have the same experience you do, I get some intellisense…sometimes. C’est la vie. Maybe we should create an “Occupy Microsoft” web blog to raise awareness of our plight…nevermind forget I said that. :)

        • http://www.tekigo.com Franck

          If I may, you need to do a couple of things in order to get intellisense natively :
          - you need to name your module (first argument of the define())
          - you need to complete _references.js with
          ///
          requirejs.config({
          baseUrl: ‘~/App/’
          });
          - put require-intellisense (https://github.com/jrburke/requirejs-intellisense) in your durandal\amd folder

          And you’re good to go :)

          By the way John, good work on your course, as always !

  • Carl

    Hi John

    Just finished module 7 of the course. It’s great – learning so much. A minor piece of feedback for you: Module 7 seems a bit rushed – you charge through the content so quickly (lots to cover, I know) and it’s meant that I’ve had to rewind lots of times to catch up/understand. Also there are concepts introduced (such as the functionality contained within Q) that probably merits a course in its own right, but is confusing to have to accept it on a “it just works” kind of basis!

    It may be covered at a later point in the course, but just in case it’s not and whilst it’s fresh in my mind, this is a common issue/problem with master/detail and I’m not sure if it can be addressed or not. When scrolling down the list of sessions in the sessions page, say I’m a third of the way down the page, I select a session and see the detail. I then “go back” either by the button or the browser back button and I’m right back up top of the sessions list again. It doesn’t retain the memory of where it was in the list. Is this fixable?

    • John

      Carl

      Thanks for the feedback. I hear you on the pace. The good news is you can stop it and try it out yourself as you go,as you mentioned. There are some off-shoots like Q and promises in the Jump Start beginner course that I don’t cover deeply. This was intentional as it could easily have become a rabbit hole that would cloud the overall focus for the course: to build a SPA. Your point is well taken that these topics deserve their own attention too, and I will get to some of these in the future. It was a balance I tried to reach to make this course be more about how to build an app quickly. The intermediate course covers a lot more “why” … as I build a lot manually (all that data service stuff). I could have gone down all these paths, but I felt it would have been too deep and too long for this course. So I decided to add those things to a future SPA Tips course, which I have already approached Pluralsight about :)

      Good point on the master detail. There are a few ways to handle this. One is to not show 100+ items in a list and instead to allow filtering or paging (like I filtered the list in the intermediate course in the favorites). The other is to store the cursor position and auto scroll on arrival. Some vendor controls will do this for you , but you can do it too. Perhaps that is a good tip for my Tips course. I’ll toss it on my list and see if it makes the cut.

  • Colin

    Hi John,

    I’m really enjoying the course.
    I’ve hit a snag launching a details page from a list page, using the bindEventToList technique that you showed. I’m clicking a row and reaching the router.navigateTo(url), but this causes a route not found error.

    I’m hard coding the url as #/opportunitydetail/123
    But I’m getting
    ["No Route Found", "/#/opportunitydetail/123", Sammy.Object] in the console.

    the opportunitydetail.js is in the viewmodels folder

    Any ideas?

    • Colin

      Got it running now.
      I was missing a route entry for this page.

      router.mapRoute(‘#/opportunitydetail/:id’, ‘viewmodels/opportunitydetail’);

      • Colin

        or even

        router.mapRoute(‘opportunitydetail/:id’);

  • Luciano

    John,

    How do i get that javascript Intelicence that u have?

    • Carl

      Luciano, it’s dsicussed a few posts up. Look for “Paul on March 19, 2013 at 12:52 pm said:”

  • Dan

    Hi John

    Really nice course, i learn a lot new stuff. I’ve seen you use normal HTML pages (.html) to represent a view against MVC 4 View Pages (.cshtml) + Controller. How would you implement a file upload?

    • Dan D

      I’d be very interested in an answer to this as I’m having some issues trying to find a solution

      • http://www.binoot.com Binu Thayamkery

        I use “fine uploader” jquery plugin for file upload, to implement this within your SPA is pretty straight forward, here are the steps,
        1) Download plugin from http://fineuploader.com/ :)
        2) Include that in your projects under \Scripts folder
        3) Include that into your bundle (vendor bundle as in John’s example)
        bundles.Add(
        new ScriptBundle(“~/scripts/vendor”)
        .Include(“~/Scripts/jquery.fineuploader-3.2.min.js”));
        Note: I have shown only this particular file.
        4)Write your MVC Controller to handle your uploaded file. (below shown code may have my specific stuff; i am storing the file to an azure storage as you can see)
        public class FileUploadController : ControllerBase
        {

        public ActionResult Index()
        {
        var response = new UploadResult();
        try
        {
        var storageAccount = CloudStorageAccount.Parse(ConfigurationManager.ConnectionStrings["StorageConnection"].ConnectionString);
        var blobStorage = storageAccount.CreateCloudBlobClient();
        CloudBlobContainer container = blobStorage.GetContainerReference(“sessiondocs”);
        if (container.CreateIfNotExists())
        {
        // configure container for public access
        var permissions = container.GetPermissions();
        permissions.PublicAccess = BlobContainerPublicAccessType.Container;
        container.SetPermissions(permissions);
        }
        var fileInfos = new List();
        foreach (string file in base.Request.Files)
        {
        var hpf = Request.Files[file];
        if (hpf == null) continue;
        var uniqueBlobName = string.Format(“doc_{0}{1}”, Guid.NewGuid().ToString(), Path.GetExtension(hpf.FileName));
        var blob = container.GetBlockBlobReference(uniqueBlobName);
        blob.Properties.ContentType = hpf.ContentType;
        blob.UploadFromStream(hpf.InputStream);
        fileInfos.Add(new UploadFileInfo
        {
        UserFileName = hpf.FileName,
        FileName = uniqueBlobName,
        FileUri = blob.Uri.ToString()
        });
        }
        response.Error = false;
        response.Success = true;
        response.FileInfo = fileInfos;
        }
        catch (Exception ex)
        {
        response.Error = true;
        response.Success = false;
        }

        return Json(response);
        }

        }

        5) Use your Uploader in your view (html) file and handle it within your viewmodel (js) file.

        View:

         Click to Upload

        ViewModel:

        Add this code in your viewAttached event, endpoint here is your MVC controller endpoint

        var fileUploaderRequester = new qq.FineUploader({
        element: $(‘#file-uploader’)[0],
        request: {
        endpoint: ‘/fileupload’
        },
        autoUpload: false,
        text: {
        uploadButton: ‘ Select Files’
        },
        validation: {
        allowedExtensions: ['doc', 'docx', 'pdf', 'txt', 'jpg', 'gif', 'png', 'bmp']
        },
        callbacks: {
        onSubmit: function (id, fileName) {
        },
        onCancel: function (id, fileName) {
        },
        onComplete: function (id, fileName, responseJson) {
        if (responseJson.success === true) {
        //my code specific !!!
        var fileDataNode = $(‘#file-uploader’)[0];
        $.data(fileDataNode, ‘fileinfo’, responseJson.fileInfo);
        }
        }
        },
        failedUploadTextDisplay: {
        mode: ‘custom’,
        maxChars: 40,
        responseProperty: ‘error’,
        enableTooltip: true
        },
        showMessage: function (message) {
        // Using Bootstrap’s classes
        $(‘#file-uploader’).append(” + message + ”);
        },
        debug: false
        });

        $(‘#triggerUpload’).click(function () {
        fileUploaderRequester.uploadStoredFiles();
        });

        I think that’s it/
        Basically approach should work with any uploader plugin, key is to initialize the plugin in viewAttached event in your viewmodel and write an end point to get the file and do something with it.

  • Gouri Sankar

    Hi John,

    While running this application in it is working fine in Chrome and Mozilla, but when i am trying in IE8.0, it throws error in breeze.debug.js file. Below is the exact code where it fails. The last return statement breaks here. Any clue what needs to be done? Also tried to find out the issue in google, and got the answer to put “content=”IE=edge” in index.cshtml , but no luck even after putting the same.

    function exec(self) {
    // clear off last one if null
    var contexts = self._contexts;
    if (contexts[contexts.length - 1] == null) {
    contexts.pop();
    }
    if (contexts.length === 0) {
    return undefined;
    }
    return contexts.some(function (context) {
    return context.fn(context, self.v);
    });
    }

  • Russ

    Mr. Papa. How does one load ko.lite javascript libraries using only requirejs (not putting script tags in the HTML)?

  • Binu

    Hi John,
    Enjoyed the course, very well done!
    I have couple of questions regarding the bundling of the scripts under /app (SPA itself). Whats the best way to bundle and minnify these? And how do we version scripts release to release? I want to get the browser cache busted when my new release goes out.
    (I am thinking using Web.Optimization and pushing it to browser earlier will defeat the purpose of AMD, even though it combines, minifies and versions)

    I would love to hear your suggestions.

    Thank you,
    Binu

    • John

      Binu – You can bundle the files using the asp.net web opt, as I do in the course. You can also use the durandal optimizer.exe to bundle and minify the app folder files.

      • Binu

        Thanks John. In your jump-start course, you do not bundle the app js files, but you do that in your intermediate course. My thinking is for a large application, bundling everything and delivering it to browser early could affect the load times for first page. If I don’t do this, i guess i can minify the app js files as part of build and deploy, but that will not version my filles (not sure if i stick verion at the end of js file name will mess with the code)…

        or am I overthinking this?

        your guidance is appreciated :)

        Also, you created the intermediate course first, and then jump-start (with durandal, etc.), could you please blog on – what all will you do differently if you design your intermediate course today (given that you have durandal, etc. now )

        Thanks!

        • John

          Binu – I didnt use optimizer.exe for the app folder in my course, but you certainly could. It will be in my tips course :) I have no problem bundling it all up front if the app suffers no perf issues from it (always try it first). The bundle would have to be massive to be a drag. I doubt you will see that.

          I wouldn’t change anything in the intermediate course if I wrote it today. I think it is very valuable to know how to work without breeze and durandal, and that course shows it. Breeze in particular adds a ton of value and replaces a bunch of javascript for me while adding more features than I wrote. But if you never saw me write it in that course, you might not believe me :) Durandal offers feature I hand coded, as well.

          • Binu

            Thanks John. Appreciate your reply. I have a suggestion for a new blog post for you, “Unit testing and debugging your SPA” :)

            Also will look forward for your Tips Course ;-)

          • Alex Bostic

            Which tips course? I’ve been trying to follow examples of how to execute optimizer.exe but have to be able to generate the .js file. Any good resources if your tips course is not out yet?

  • John

    I was wondering why you sometimes define functions such as:

    var refresh = function () {
    // Do refresh stuff here
    };

    and other times:

    function refresh() {
    // Do refresh stuff here
    }

    Do you do that so that all public ones are var name = function () {} and private are function name() {}?

    Is there any benefit to using var name = function() {} over function name() {}? It seems that since function name() {} gets defined earlier you would run into less issues, or am I missing something?

    Thanks!

    • John

      John – When I want to hide a function as an internal method, a convention I have been using is to put the function statement below the return. Sometimes I want to see if more prominently, so I raise it up and make it an expression. Often, it really doesn’t matter which you choose as long as you don’t violate any JavaScript rules.

      • John

        Roger, I was just wondering if there was a benefit to using var name = function(). function name() {} gives you the name of the function when debugging, and doesn’t die if you reference name() earlier in your code. I understand why you do the above / below the viewmodel return to separate out public vs private declaration areas, was just wondering if there is a benefit to the var name = function() {}. Awesome job on the course! :)

  • Anders

    Hi,

    any recommendations on using/not using breeze for us people not working with Entity Framework? We already have a service facade which we would like to build upon and below that we use nHibernate as ORM. We would still like to accomplish a rest api as an extra benefit next to the SPA.

    Thanks for an interesting course btw

    Anders

  • http://www.DansGreenShoes.com Dan Hickman

    John, great video and I continue to learn a lot. Much appreciated. Did you consider merging sessionadd and sessiondetail into one view/vm? If you did consider it, what were the pros/cons that lead you to keep separate?

    • John

      Dan – Thanks, appreciate the feedback. I did consider it and frankly it could certainly have gone that way with a single view and viewmodel for add/edit/delete and possibly view/read only. I decided to keep it separate to make it easier to digest. It is perfectly valid either way, and in this case a lot of the viewmodel was different for insert vs edit. The view, not much at all changed. So choose what makes sense in your app.

      I also created a version of the insert view that works inside of a modal dialog. I will include that in a future course on SPA, but that might give you some insight on why I prefer separation.

  • Em

    Hi John – I’m working on the SPA Jumpstart Video and need to say thanks. I’ve learned more from your courses than most I’ve had at uni.

    I’ve built my application following your SPA guidance, however I’m now at a point where I need to authorize/authenticate users. I’d like to keep this all within the SPA via JSON requests to my WebAPI server instead of using an MVC client along with my SPA, however I’m not sure how to get started. Would you recommend beginning with the MVC SPA template and converting their AccountController to inheriting from ApiController to serve JSON, or is there a better approach? Perhaps there’s another video of yours that I haven’t come across – I’d appreciate any help you can give! Thanks so much for your time.

    • John

      Em – That’s a fine approach. The ASP.NET SPA template has auth in it so you could start with what they did. I’ll expand on this topic in an upcoming course in 2013, too

      • Em

        Thanks John!

  • Bill

    John,
    I’ve watched both your SPA course on Pluralsight. They’re good. But, I’ve been wondering about your client side caching of data in a multi-user environment.

    For your videos, it all works because you’re the only user. Also, for lookups, caching the data in the client makes sense. The data doesn’t change so why go back to the server every time to refresh the data.

    However, for CRUD operations (mainly CUD) in a multi-user scenario, one user may insert or update or delete, while all the other users do not see those operations because they’re using their version of cached data when they started the app or navigated to the view. So, the updates to the underlying database data are not reflected to all users.

    Am I missing something?

    How do you propose to handle this multi-user scenario for data that changes?

    I’m sure you wouldn’t expect an end-user to always have to remember to click a refresh button. Right?

    Or, for ‘volatile’ data, do you not use client-side caching of data (i.e., every you go to the view, you go to the server and get the latest data)?

    Thanks.

    • John

      Bill – Great questions! I’ll be hitting more of these topics in an upcoming SPA Tips course in 2013.

      Caching data: When the data is cached on the client, you need to account for changes on the server from other users. Usually this is handled via optimistic concurrency in a data layer on the server. You try to save, you realize the data changed (your data layer will tell you), so you notify your user and let them decide what to do (refresh, etc). Another option is to use something like SignalR to notify the user when the data changes, and either ask them to refresh, or refresh it for them. There are many strategies, you just need to choose the one that works for you.

      Lookup data: I classify lookup data as data that doesn’t change. In CCJS that data is the rooms, tracks, time slots. It is very unlikely that they will change. So I get that data and cache it and dont look back. However, if the data is possible to change, maybe a new room opens up, I could use one of the techniques I listed above to handle it.

      Volatile data: If the data changes all the time, like stock quotes, I would certainly use something like SignalR to refresh it. But that’s an extreme data changing example (every 5 seconds perhaps). Other data, like orders, customers, events, people, whatever … they change in an app. I’d still cache the data in Breeze and the datacontext, and then employ one of the strategies above.

      I hope this helps!

      • http://www.breezejs.com Ward Bell

        Good choices, John. An additional one: you could auto-refresh data on a schedule pinned to volatility. You stash a “last updated time” somewhere and decide for each class of data as appropriate. It’s a complementary tool on your belt.

  • David

    Hi John,
    Thanks for another great course. I completed your original SPA course and have really enjoyed how that knowledge gave me insight into the functionality of Breeze and Durandal. I am curious, however as to why you didn’t implement a repository / unit of work pattern as per your original SPA course. Perhaps just because it is more advanced? Most importantly, would you recommend implementing that pattern for a production application using Breeze? Are the advantages of that pattern somehow negated by Breeze?
    Many thanks,
    Dave

    • John

      David – You can certainly use separation patterns on the back end with Durandal and Breeze on the client. Those patterns were not critical to the beginner course, as I wanted to focus all eyes on the client. But by all means go ahead and create them on the server if you like. Thanks for the feedback. I am glad you enjoyed the courses.

  • Joshua Beall

    Hi John,

    I’m working through your “Single Page Apps JumpStart” on PluralSight.com. It’s been informative so far. Thanks for doing it!

    However, I ran into an issue today that really had me confounded. In the module “Navigation with Durandal”, at the beginning of the video titled “Preparing for New Views and the Router” you made the following statement:

    “Because we’re going to be adding more views and view models, I [moved them] into [subfolders] named views and viewmodels. And I also put the data service logger and model inside of a services folder. The only code changes that were required for this were pointing the different references to those folders.”

    You then gave a few examples, e.g., app.setRoot(‘shell’) changed to app.setRoot(‘viewmodels/shell’).

    This did not work for me. Durandal still expected the views and viewmodels to be in the same folder. I can see in the Chrome developer tools network tab that it’s looking for viewmodels/shell.html, which actually resides in views/shell.html.

    I’m ashamed to say it, but it took me hours to figure out what was going on. I eventually figured out that if I installed Durandal.router, and changed the main.js file from this:

    require.config({
    paths: { “text”: “durandal/amd/text” }
    });

    define(function(require) {
    var system = require(‘durandal/system’),
    app = require(‘durandal/app’);

    system.debug(true);

    app.start().then(function () {
    app.setRoot(‘viewmodels/shell’);
    });
    });

    To this:

    require.config({
    paths: { “text”: “durandal/amd/text” }
    });

    define(function(require) {
    var system = require(‘durandal/system’),
    app = require(‘durandal/app’),
    viewLocator= require(‘durandal/viewLocator’);

    system.debug(true);

    app.start().then(function () {
    viewLocator.useConvention();
    app.setRoot(‘viewmodels/shell’);
    });
    });

    Then it worked.

    The key thing was adding the two viewLocator lines–first require()ing it, then calling viewLocator.useConvention(). From my reading on the Durandal documentation, this is what tells Durandal to look for view-viewmodel pairs in the views and viewmodels folders, respectively. Otherwise, it expects the pairs to be in the same folder.

    Is this the correct solution? If so, adding a note about this to your course could help save future students some frustration!

    If it’s not the correct solution, I’d like to know what I was supposed to do.

    Thanks!

    -Josh

  • Ronald Bastien

    Hi John, great course on pluralsight. I’m using it as a framework for a LOB application I’m currently writing.

    I have one issue I haven’t been able to solve.

    In createNullos, I want the id of the lookup to be null since my model supports a null foreign key. Here’s how I’m doing it:

    createNullo(entityNames.Bed, {id: null, bedCode: ‘…’});

    At first, it works great. My drop down has the Nullo record. The save also works great, it saves a null foreign key to the database. Then “kaboom”, when I return to my list of entities, all entities have disappeared except for the new record I created.

    Even my I press refresh from my list, I do not get back the data from the database. The database however contains all records, including the new one with the null FK.

    Any ideas?

    • Ronald Bastien

      Sorry John,

      Figured it out, it was a bad biding on the return to the list because of the null. Lessons learned, check the console properly before posting.

      Just for the record, including a null key for optional FK works great.

      Keep up the good work and I look forward to your next courses.

      • Ronald Bastien

        Hi john,

        Just to help out the readers, if you want to have a null FK with the NullO lookups, you need to use undefined for the key instead of null as I previously stated.

        My entity was always “dirty” after my add.I couldn’t understand why until I figured out that my FK was being changed from null to undefined, therefore triggering the hasChangesChanged. By setting the id to undefined instead of null, it solves the problem.

        Before:
        createNullo(entityNames.Bed, { bedId: null, bedCode: ‘…’ });

        After:
        createNullo(entityNames.Bed, { bedId: undefined, bedCode: ‘…’ });

        I hope this helps someone out.

  • Marc

    What will be the best way to reset or sync the lookups when records get updated?

    • John

      Marc – If it is truly a lookup static list that doesnt change, then there is no need. If the data is very volatile, I wouldn;t make it a lookup. If it is data that doesnt change often you could require a refresh of the app. Things like states, countries, etc. If it is something that changes and you want to refresh it, you could use signalR to do this. The key is findint he hook to tell signalR to fire. A good place can be the business logic that saves this new data could fire off a signalR message which tells the client about the new data.

  • Anders

    Hi,

    I have this scenario where i have multiple attributes for one item. So I pass the item it’s collection of attributes to the view model. Now the deal is that I would like to display all attributes in the view but they can have different types and so require different controls. I had a look at widgets in durandal but could not figure out a way to dynamically pick a widget type based on my data. Any ideas on how to work around something like this?

    • Anders

      Well what i finally did was to hook up to viewAttached and to started to manipulate the DOM with JQuery. Should I feel ashamed?

      • Anders

        For some reason this approach of modifying the dom with jQuery and re-applying the knockout bindings on the modified content only work for IE but not for Firefox or Safari. The dom mods are displayed but the knockout bindings aren’t working anymore. Anyone knowing why?

        And I would still like to learn a different way of producing dynamic views…

        Anders

        • John

          I don’t use jQuery to manipulate the DOM, I use Knockout for bindings. Using both is not my recommended approach. I’m not sure why you wouldn’t just use data binding to bind the view model properties to different controls. If its a complex control, create a binding handler for it. If you need a Durandal widget, still use data binding. Best advice I can give is top open a StackOverflow question for it and provide lots of examples and running code in jsfiddle or plnkr.co

          • Anders

            Hi John,
            the root problem is that I don’t know which html controls (or widgets) to display before I see the actual data. This is what I use JQuery for. But you suggest that this manipulation is also possible via a binding handler if I got it right? I’ll ask around on stack overflow.. Thanks for your response.

            /Anders

          • SiddharthP

            Hi Anders,

            I have got a similar case to load widgets based on the json data i.e. a unique control id coming from the server and adding all those widgets on the fly in one view. Could you please tell me how you managed to achieve this kind of behaviour ?
            Sid

          • Anders Emmerich

            Hi Sid,

            what I did was to create one html snippet (placed in a
            sub folder attributeviews) for each type of control (=the
            attributeType() below) I needed and then use ko compose to bind them:


            I hope this helps

            Anders

          • SiddharthP

            Hi Anders, whatever you posted as sample is not visible here. Could you please create a gist or a working sample in jsfiddle whenever you have time or any resource explaining this conecept? It will be really helpful. Cheers

          • Anders Emmerich

            This got lost:
            tbody id=”attributes” data-bind=”foreach: attributes”
            tr
            td data-bind=”text: name”/td
            td colspan=”3″
            !– ko compose: {view: ‘attributeviews/’ + attributeType()} –
            !–/ko–
            /td
            /tr
            /tbody

          • Anders

            The solution was simple enough, I used the compose binding on a property of the data to select the sub view

  • http://www.myspa.somee.com/ Stefano

    Hi John, I am doing your Single Page Apps JumpStart course, really great! I have problems to deploy the course samples projects (4-spajs-m4-adding-a-view-exercise-files). I deployed the to http://www.myspa.somee.com/ and the app hangs with this “Uncaught SyntaxError: Unexpected token * vendor:9 Uncaught ReferenceError: $ is not defined” error. This does not happen with a CCJS really from scratch. I cannot figure out what it is happening with the sample projects. Did you try to deploy any CCJS samples? Thank you. Ciao, Stefano

    • http://skilltraxx.com James Fleming

      $ not defined has often meant that JQuery hasn’t been added to the project or is in the wrong order.

  • http://skilltraxx.com James Fleming

    Question about dtoToEntityMapper classes.

    I seem to be getting different behaviors depending upon how I query via breeze.

    If I use a select clause (note Company is an entity:
    var query = EntityQuery.from(‘Positions’)
    .select(‘id, endDate, gauge, hoursPerWeek, memberId, title, summary, startDate, totalHours, weightedHours, company’)
    .where(‘memberId’, ‘==’, id)

    the query succeed call to mapToEntity works just fine.

    If I simply call with out the select i get an error: “entityAspect is undefined”
    by my queries without out select all fail.
    var query = EntityQuery.from(‘Positions’)
    .where(‘memberId’, ‘==’, id)

    My IQueryable

    function mapToEntity(entity, dto) {
    // entity is an object with observables
    // dto is from json
    for (var prop in dto) {
    if (dto.hasOwnProperty(prop) && prop !=”entityAspect”) {
    entity[prop](dto[prop]);
    }
    }
    return entity;
    }

    when stepping through Q

    function _fulfilled(value) {
    try {
    return typeof fulfilled === “function” ? fulfilled(value) : value;
    } catch (exception) {
    return reject(exception);
    }
    }

    I get an error from Breeze debug on line 4212
    “TypeError: newValue.entityAspect is undefined”

    I see the navigation property in my JSON, the data is there, and appears to be a dependent observable. I just don’t know what I need to do to use a breeze query that returns a complex object graph (both the primary entity and children (and children collections) so I can extend this SPA.
    I have tried to call my IQueryable with and without includes:

    public IQueryable Positions()
    {
    return _contextProvider.Context.Positions.Include(“Company”)
    .Include(“ChartVals”).Include(“Tags”);
    }

  • Jesse P

    I would like for my app to have 2 different entry points. The default one being (http://localhost) and then the second one being (http://localhost/parameter1/parameter2). How would I go about setting this up in an app using the current example?

  • http://skilltraxx.com James Fleming

    This forum has been a great source for answers/inspiration for projects based on the SPA Hot towel.
    Although I must admit I’ve been finding myself in hot water as much as enjoying the hot towel…!

    My latest bit of angst is trying to deal with promises and DOM injection.

    I’ve got Breeze returning my data and I’m calling my javascript method to inject the DOM with a tag cloud (one cloud per row), but the problem is that my DOM target doesn’t yet exist, even though I’m calling it at the end of my activation event with a promise.

    Any ideas how to make this behave?

    Question posted on SO

    http://stackoverflow.com/questions/16367578/dom-injection-and-knockout-timing-issues

%d bloggers like this: