Jump-Start Single Page Apps | John Papa

John Papa

Evangelist on the loose

Jump-Start Single Page Apps

posted by John with 84 comments

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

84 comments Hide comments

Kevin on said:

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 on said:

    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 on said:

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 on said:

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 on said:

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 on said:

    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 on said:

John,

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

    John on said:

    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 on said:

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 on said:

    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 :)

Chris G on said:

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 on said:

    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 on said:

      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 on said:

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

Chris G on said:

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 on said:

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 on said:

    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 on said:

      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 on said:

    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 on said:

      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!

Paul on said:

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 on said:

    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 on said:

    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 on said:

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 on said:

    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 on said:

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

Paul on said:

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 on said:

    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)

      Paul on said:

      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. :)

        Franck on said:

        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 on said:

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 on said:

    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 on said:

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 on said:

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

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

    Carl on said:

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

Dan on said:

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 on said:

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

      Binu Thayamkery on said:

      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 on said:

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 on said:

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

Binu on said:

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 on said:

    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 on said:

      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 on said:

        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 on said:

          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 ;-)

John on said:

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 on said:

    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 on said:

      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 on said:

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

Dan Hickman on said:

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 on said:

    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 on said:

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 on said:

    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

Bill on said:

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 on said:

    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!

      Ward Bell on said:

      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 on said:

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 on said:

    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 on said:

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 on said:

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 on said:

    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 on said:

      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.

    John on said:

    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 on said:

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 on said:

    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 on said:

      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 on said:

        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 on said:

          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

          Anders on said:

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

Stefano on said:

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

James Fleming on said:

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”);
}

James Fleming on said:

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

Leave a Reply


(required)


(required)



*