Making things do stuff

Ractive Webpack Component Loader

June 7, 2016 by Tim Schottler

Update - I may be switching from webpack to rollup, which is written by Rich Harris as well. I didn't find rollup until I tried figuring out how to make this component loader work with es6. Oops.

I've switched from ractive-loader to the ractive-component-loader, and it's pretty dandy. If you're not familiar with setting up webpack or a webpack loader, read my previous post about ractive-loader for a sample webpack config. If you would prefer to keep your templates separate from your component's logic, that post is probably more appropriate for you, too.

Instead of having, for example... Loading.js, and Loading.html, I now have just Loading.html:

<p>
	<i class="fa fa-spinner fa-pulse fa-2x fa-fw" /> <!-- font-awesome spinning icon -->
	{{message}}
</p>

<script>
	component.exports = {
		data: function () {
			return { message: 'Loading...' }; // default message
		}
	};
</script>

The ractive-component-loader for webpack compiles the html template portion to javascript, sets it into component.exports.template, and sets module.exports = Ractive.extend(component.exports). So now I can use my Loading component like so:

var Loading = require('./Loading.html');
new Loading({ el: '#SomeContainer' });

Or, from other components...

<link rel="ractive" name="Loading" href="./Loading.html" />

<h1>App</h1>
{{#if !user && !error}} <!-- if the user data isn't loaded and there's no error set, show loading message -->
	<Loading message="Loading profile data..." />
{{/if}

<script>
	var userService = require('../services/UserService.js');

	component.exports = {
		oninit: function(){
			var me = this; // could also use bind on handler

			// pretend the user service returns a jQuery 
			userService.getCurrentUser().done(function(response){
				me.set('user', response);
			}).error(function(response){
				me.set('error', response.responseJSON);
			});
		}
	};
</script>

Couple things:

  • ractive-component-loader compiles your template, sets it into component.exports.template, then sets module.exports = Ractive.extend(component.exports)
  • any tags will be set into component.exports.components by name
  • it also looks for styles and will set them into component.css, if you're doing that.
  • Note that the github README documentation on resolving child components is a bit off. You use a <link ... /> tag, not an <import .../> tag.
  • Also note that you can also still use components: { name: value } here, if you need to do something more complicated, like returning a string name of a component from a function (eg: http://stackoverflow.com/questions/31075341/how-to-create-ractives-subcomponents-dynamically-and-change-them-programmatical)
Continue reading...

.NET Core RC2 WebDeploy to IIS with VS

June 6, 2016 by Tim Schottler

I deployed a .NET core RC2 app to a Windows / IIS machine running on EC2 today. Ran into a few issues and decided to write about it. This article got me most of the way there, and I encourage you to start there, but I ran into a few more issues.

First, configure IIS:

  1. Add a new website in IIS. Set its Physical Path to an empty folder of your choosing.
    • The article above encourages you to create a logs directory inside this path. Good a place as any for them.
    • Note that your wwwroot (or whatever folder you've configured in lieu of it) will get published inside of your IIS site's Physical Path. IIS will not use your wwwroot for its physical path - it merely hands off requests to Kestrel, which will serve static files from that physical path. If you've ever configured IIS to front for Tomcat, Jetty, stuff like that, this is exactly the same.
  2. Configure your new website to allow web deploy publishing.
  3. Copy the USERNAME_SITENAME.PublishSettings file created in the previous step to your local machine.
  4. Install .NET Core Windows Server Hosting on your server.
  5. Open a cmd prompt and run iisreset - note, this will restart all of your IIS sites, but it's needed to get IIS to recognize dotnet being added to the windows Path.
  6. After these steps, I was getting 502 gateway errors. Running via command line yielded this error (Did not find a suitable dotnet SDK at...). After trying a few other things, I ended up just installing the .NET Core SDK for Windows on the server, then everything worked.

Next, configure your VS project

  1. Open your project in Visual Studio, right click the project name, and click Publish.
    1. Click Import and choose the .PublishSettings file generated by your server.
    2. Double check the value in the server field is actually externally accessible - IIS might've generated https://machinename:8172/ or something - I just use "www.tim12.com" for example.
    3. Validate Connection - if it doesn't go through, you likely messed up configuring Web Deploy in step 2, or you're hitting either Windows or network firewalls - make sure TCP port 8172 inbound is open on your server. If you're using EC2 like me, go to your Security Groups and add an Inbound rule for TCP port 8172 - I lock it down to only allow access from my IP, but if your IP changes often and you deploy often, that can get annoying.
  2. Once your VS Publish Settings can Validate Connection successfully, move on to previewing the publish. You should see a list of all the files that need to go up to your server. Hit Publish once you've reviewed them.
    • For me, this failed with server SSL certificate problems. You can either make your machine trust your server's certificates, or just edit your project's Properties > PublishProfiles > YOURPUBLISHPROFILE.pubxml and add... <AllowUntrustedCertificate>True</AllowUntrustedCertificate> inside the <PropertyGroup> tag.
  3. Once publish completes, victory should be yours. Try hitting your site.

If you still have problems, this GitHub issue has a lot of potential issues / explanations / solutions

Continue reading...

Ractive Webpack Template Loader

June 6, 2016 by Tim Schottler

Update - if you want to use the ractive component spec instead of just parsing html templates and keeping component logic in a separate file, read this instead.

First grab the ractive-loader node package. Then use it in your webpack.config.js. Here's a stripped down sample configuration that will pre-compile Ractive .html templates for you using the ractive-loader node package:

module.exports = {
	entry: {
		app: './Client/js/App.js'
	},
	output: { filename: './wwwroot/js/[name].bundle.js' },
	module: {
		loaders: [
			{ test: /\.html$/, loader: 'ractive' }
		]
	},
	resolve: {
		extensions: ['', '.js', '.html']
	}
};

Then when webpack sees something like this...

App.js

var App = new Ractive({
	el: '#app',
	template: require('./App.html'),
	data: {
		title: 'Donkey Kong',
		heroes: [ 'Me', 'You', 'That other one' ]
	}
}); 

App.html

<h1>{{title}}</h1>
<ul>
	{{#each heroes}
		<li>{{.}}</li>
	{{/each}
</ul>

It will pre-compile App.html to ractive's js template format, and it's injected the same way requiring any other js module works.

If you're new to webpack, here's a webpack.config.js that'll do a few more useful things. Hopefully the comments explain enough.

var webpack = require('webpack');

// splits anything in the vendor entry point into a chunk that isn't duplicated into every other entrypoint
var chunkPlugin = new webpack.optimize.CommonsChunkPlugin(
	/* chunkName= */'vendor',
	/* filename= */'./wwwroot/js/vendor.bundle.js'
);

// minify javascript
var minifyPlugin = new webpack.optimize.UglifyJsPlugin({
	compress: {
		warnings: false
	}
});

// lazymode so we don't have to require Ractive in every component / endpoint / etc
var providerPlugin = new webpack.ProvidePlugin({
	Ractive: 'ractive'
});

module.exports = {
	entry: {
		vendor: [
			// vendor will include these packages
			// CommonsChunkPlugin tells the other entry bundles not to include anything that vendor includes
			'jquery',
			'ms-signalr-client',
			'ractive',
			'ractive-datetime',
			'bootstrap-sass',
			'grapnel'
		],

		// this app just has a couple entry points - one with login / register stuff, another for handling SSO redirects back, and a secure app
		public: './Client/js/Public.js',
		externalLoginConfirmation: './Client/js/ExternalLoginConfirmation.js',
		app: './Client/js/App.js'
	},
	output: { filename: './wwwroot/js/[name].bundle.js' },
	module: {
		loaders: [
			// compile ractive html into ractive js
			{ test: /\.html$/, loader: 'ractive' },

			// make $ and jQuery available globally - some packages are poopy and rely on it
			{ test: /jquery\.js$/, loader: 'expose?$' },
			{ test: /jquery\.js$/, loader: 'expose?jQuery' } 		]
	},
	plugins: [
		chunkPlugin,
		providerPlugin,
		minifyPlugin
	],
	resolve: {
		extensions: ['', '.js', '.html']
	}
};
Continue reading...

SignalR with .NET Core RC2

June 6, 2016 by Tim Schottler

Update - The bugs addressed in this article are not present in .net core 1.0! Will update more later.

I wanted to try SignalR with .NET Core RC2, but according to the .NET Core Roadmap, it isn't officially supported yet. Let's try it anyway. (TLDR - I do get it working, but only sending to all clients, so far)

First, add the nuget packages. If you don't already have it in there, add the aspnetmaster nuget package source first.

Then add to your project.json:

"Microsoft.AspNetCore.SignalR.Server": "0.1.0-rc2-final",
"Microsoft.AspNetCore.WebSockets.Server": "0.1.0-*"

Then make a simple hub...

using Microsoft.AspNetCore.SignalR;
using Microsoft.AspNetCore.SignalR.Hubs;

namespace TestProject.Hubs {
	[HubName("testHub")]
	public class TestHub : Hub {
	}
}

Then use it somewhere... for example, this controller's Index action would call hello("World") on all connected clients.

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;
using Microsoft.AspNetCore.SignalR.Infrastructure;
using TestProject.Hubs;

namespace TestProject.Controllers {
	public class HomeController : Controller {
		private readonly IHubContext _hub;

		public HomeController(IConnectionManager connectionManager) {
			_hub = connectionManager.GetHubContext<TestHub>();
		}

		public IActionResult Index() {
			_hub.Clients.All.hello("World");
			return View();
		}
	}
}

Then set up a view that included the MS jQuery SignalR client, and /signalr/hubs. If you haven't done this before, read more on the Microsoft jQuery SignalR client here

<script src="/scripts/jquery.signalR-2.1.0.min.js"></script>
<script src="~/signalr/hubs"></script>

Neato, that should work, right?

No. It should, but it doesn't. Open your console, and you'll see a javascript error saying the hub is undefined / not found / something along those lines (I forget).

If you hit /signalr/hubs, you'll notice that it doesn't actually have any hubs configured. Why's that? Because SignalR is broken and doesn't load any hubs from your own project's assembly. A few people figured it out in this GitHub issue.

Two ways to fix it...

  1. Add https://www.myget.org/F/codecomb-rc2/api/v3/index.json nuget package source, and let it replace the aspnetmaster version of the SignalR package.
  2. Or make your own IAssemblyLocator and set it into IoC.
    using Microsoft.AspNetCore.SignalR.Hubs;
    using System.Reflection;
    using System.Collections.Generic;
    
    namespace TestProject.Services {
    	public class CurrentAssemblyLocator : IAssemblyLocator {
    		public IList<Assembly> GetAssemblies() {
    			return new[] { Assembly.GetEntryAssembly() };
    		}
    	}
    }
    
    Then in Startup.ConfigureServices...
    services.AddSingleton<IAssemblyLocator, CurrentAssemblyLocator>();
    

Both routes work, hopefully neither will be necessary soon. I opted for the nuget package once I saw someone had done that, as that at least won't involve code changes, just removing a package source.

That should get you started at least. I'm still having problems trying to get [Authorize] working on Hubs, but it's not enough of a priority to figure out yet. If anyone else has gotten [Authorize] and sending to individual clients by Id / Username / whatever, please let me know!

Continue reading...

Binding to .NET ModelState with Ractive

June 6, 2016 by Tim Schottler

I liked how validation messages were done with Razor, particularly with the tag helper improvements in .NET Core.

<!-- Renders any errors not related to a specific field -->
<div asp-validation-summary="ModelOnly"></div>

<form asp-controller="Account" asp-action="Login">
	<input asp-for="Email" />
	<!-- render errors for the Email field -->
	<span asp-validation-for="Email"></span>
	<!-- more fields, submit button, etc -->
</form>

To implement something similar in an app using Ractive for components...

  1. All my .NET API actions return the ModelState (usually via return BadRequest(ModelState) for a 400 status code). ModelState gets serialized to basically a Map<string, string[]>, where the key is the field name, and the value is a list of problems to show the user for that field. Then we just use an empty field name for errors related to the overall submission. Here's an example of what a .NET ModelState response might look like from an API:

    {
    	"": [
    		"Generic Error 1",
    		"Generic Error 2"
    	],
    	"Email": [
    		"Email is required"
    	]
    }
    
  2. When making API calls from Ractive components, set the response into the Ractive instance data. Using jQuery directly for a simple example:

    module.exports = new Ractive({
    	template: require('./Views/App.html'),
    	el: '#EntryPoint'
    	components: {
    		ModelStateError: require('./Components/ModelStateError.js')
    	},
    	oninit: function(){
    		this.on('login', function(event){
    			$.post('/login', {
    				email: event.context.email,
    				password: event.context.password
    			}).done(function(response){
    				// yay.. do whatever
    			}).error(function(response){
    				this.set('modelState', response.responseJSON);
    			});
    		});
    	}
    });
    
  3. Use a child component that renders model state errors where you want them in your template: App.html - Note that I'm not actually passing modelState to the tags... because I've made this a convention throughout the entire app, I rely on Ractive's default behavior of inheriting parent data instead of redundantly passing modelState all over.

    <ModelStateError />
    
    <form on-submit="login">
    	<input type="text" name="email" value="{{email}}" />
    	<ModelStateError name="email" />
    
    	<!-- ... additional fields ... -->
    	<button type="submit">Submit</button>
    </form>
    

    ModelStateError.html

    {{#if modelState[name]}}
    	{{#each modelState[name]}}
    		<div class="text-danger">
    			{{.}}
    		</div>
    	{{/each}}
    {{/if}}
    

    ModelStateError.js - note the default empty string for the name property. This effectively makes it so <ModelStateError /> will render all the non-field-related errors. To render a field's errors, you'd use <ModelStateError name="Email" />, etc.

    module.exports = Ractive.extend({
    	template: require('./Views/Components/ModelStateError.html'),
    	data: function () {
    		return { name: '' };
    	}
    });
    
Continue reading...