Saturday, October 7, 2017

Vue.js, Gulp, Browserify, Vueify - Getting it running...

The state of javascript UI frameworks and setup is abysmall, but I'll save that for another post.

I like the idea of Vue, and I want to use it. I've tried getting a "flow" together a few times, but even when I was able to shake the right voodoo rattle, I hadn't been able to get a flow that worked consistently and didn't make me want to bash my computer and try making money as a basket weaver. Until now. Hopefully it'll last for more than a few weeks...

The Requirements

WARNING: I'm sure this setup will only work for specific versions of any of these packages. Installing the latest of each of these gives tons of warnings about deprecated packages, etc, which makes me afraid of deploying it in a production environment. Again, a topic for a different post.

Package.json

I've gone through a TON of tutorials trying to get this to work. With these specific versions of each package, I have this working on my machine. Because client side bundling, transpilation, etc is soo darn fragile, I can't guarantee that this will work on your machine.

//NOTE: Dependencies marked with // * might not be needed
  "devDependencies": {
    "babel-core": "^6.26.0",
    "babel-plugin-transform-runtime": "^6.23.0",
    "babel-preset-es2015": "^6.24.1",
    "browserify": "^14.4.0",
    "fs": "0.0.1-security",
    "gulp": "^3.9.1",
    "gulp-concat": "^2.6.1", // *
    "gulp-cssmin": "^0.2.0", // *
    "gulp-sourcemaps": "^2.6.1", // *
    "gulp-uglify": "^3.0.0", // *
    "gulp-webserver": "^0.9.1", // *
    "pug": "^2.0.0-rc.4",
    "vue": "^2.4.4",
    "vueify": "^9.4.1",
    "vueify-insert-css": "^1.0.0" // *
  },

Folder Structure

I know this isn't a typical folder layout, but bear with me. I'm keeping it simple to help eliminate issues relating to path. Because most modules have extremely crappy error handling. Tracking down issues here is hit or miss.

/dist
/src
   app.js
   app.vue
.babelrc
gulpfile.js
index.html
package.json

Gulp File

var gulp = require("gulp");
var fs = require("fs");
var vueify = require("vueify");
var browserify = require("browserify");

gulp.task("vueify", function(){
    return browserify('./src/app.js')
    .transform(vueify)
    .bundle()
    .pipe(fs.createWriteStream("./dist/bundle.js"))
});

app.js

var Vue = require('vue');
var App = require('./app.vue');

new Vue({
  el: '#app',
  render: function(h){
      return h(App);
  }  
});

app.vue

I added text that would be easily found in the bundle.js file.

<template lang="pug">
  .app
    h1 Test Template persimmon
    p {{title}}
</template>

<script>
export default {
    name: "app",
    data() {
        return {
            title: "Pug View Page " + new Date().toTimeString()
        }
    }
};
</script>

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Vue Gulp Tests</title>
</head>
<body>
    <p>App should be between here</p>
    <div id="app"></div>
    <p>And here</p>
    <script src="dist/bundle.js"></script>
</body>
</html>

Wednesday, August 19, 2015

Highlight Row by Cell Value in Google Sheets

I'm a big fan of Google sheets. That said, sometimes the features can be a bit confusing. I needed to highlight a row depending on the value in one of the columns. Figured out how to do it finally...


Set your range. Since we'll be using rows, specify the row you want to start with (excluding headers) and the row you want to end with.

Set the Format cells if... to "Custom Formula is"

Specify the rule. Since we're highlighting by row, you MUST use $ before the column definition. The row you specify must be the same row that you're starting with, in my case, 2.

And that's it!

Thursday, July 30, 2015

Google Sheets Banker's Rounding

I needed to have a formula for Banker's Rounding and use it in Google Doc's Sheets app. This seems to work well. This only rounds to whole numbers, but could be modified to do currency pretty easily.

=if(mod(B2,0.5)>=0,IF(ISEVEN(B2),ROUNDDOWN(B2,0),ROUNDUP(B2,0)),round(B2,0))


Tuesday, July 14, 2015

Address Regex

Needed to do some loose validation on mailing addresses. Here's what I did.

Physical Address Format

^[\d]+ [\w\.,\- ]+([\d]{5}(-[\d]{4})?)( .+)?$


Mailing Address Format

^[\w\.,\- ]+(\d{5}(-\d{4})?)( .+)?$


PO Box Format (used to exclude PO Boxes, taken from http://forums.asp.net/t/1551081.aspx?regular+expression+for+P+O+Box+validation

[P|p]*(OST|ost)*\.*\s*[O|o|0]*(ffice|FFICE)*\.*\s*[B|b][O|o|0][X|x]


NOTE: Since I'm using \w, underscores will get through this filter. I didn't think it was a big deal, but if you need to exclude underscores, it's easy to fix. The post office would apparently remove underscores if detected, as it does with all special characters other than hyphens between meaningful numbers.

I also didn't validate state, as we're feeding state through a dropdown. Regex validation would need to include every state and territory permutation (CA, Calif, California, etc...).


The last bit allows any character to be used after the zip code.

To see how the Post Office parses addresses, you can look here...

http://pe.usps.gov/cpim/ftp/pubs/Pub28/pub28.pdf

Thursday, May 21, 2015

Async in .Net - Console App

I wanted to play around with Async in a console app, but most of the examples are in either winforms or mvc.

I found this article that showed exactly how to do it! Posting it to get it more coverage and so that I can get back to it if needed.


Thursday, April 23, 2015

Add / Get Query Value from Uri Extension Method

Needed to pull some code from an older project that had an interesting utility. It had extension methods for adding and retrieving values from a Uri query string.

 I thought that was neat so I wrote my own version.


Wednesday, April 15, 2015

Angular Html Literal Directive

Yes, this is not the way you're supposed to use Angular. There might be a better workaround out there.

I needed a way to generate image elements through Angular with custom attributes, including classes and styles. I'm working in a legacy system that makes this difficult. Can't use curly braces because we need to keep using the dead jQuery Tmpl, hasn't been phased out yet. I couldn't make the angular bindHtml directive to work. It kept on removing my style and class attributes.

So I came up with this...


Monday, March 23, 2015

Angular Comparison Validation

Ahh, Angular... Why can't your documentation be as good as what I find on StackOverflow?

Thank you, Bernard Loureiro  for the code sample! From that I was able to put this together.

This directive allows you to compare two different fields on your model. The compareOperator property is really cheesie right now. If it is set, it checks to see if there's an equals and/or a greater than sign. If not, it ignores it. To make that more robust, I'd need to give some good error messages, add some documentation, etc... It works for my needs right now.


Monday, March 16, 2015

Escaping Text for CSV in TSQL

Needed string fields output in CSV, but with punctuation being used, that can be crazy. Figured it out, at least for the cases I'm looking at.


SELECT
cr.Id
,'"' + REPLACE(cr.Description, '"', '""') + '"' AS Descrption
,'"' + REPLACE(rr.Response, '"', '""') + '"' AS Response
,'"' + REPLACE(rr.Reason, '"', '""') + '"' AS Reason
,cr.ResolvedDate
FROM Reviews cr
LEFT OUTER JOIN Responses rr ON cr.Id = rr.ReviewId
ORDER BY cr.Id, rr.Id DESC

Wednesday, February 4, 2015

PrismJS - Code Formatter for blogs

Note to self: Use this when you eventually get around to styling this blog!

http://prismjs.com/

Tuesday, February 3, 2015

MVC Routing and Areas - Can't find your view?

MVC Routing is weird...

We're refactoring an app to use Areas to break up our app into more manageable pieces. I moved some controllers to the new "Internal" area and tried to access it. It couldn't find the controller by accessing ~/Internal/Controller, but my code would be hit if I did ~/Controller, even though the controller had been moved to the new Area.

Even when hit, it couldn't find the view by calling return View(model);

I added a new controller to the area controller's folder and used that namespace for my existing controller.

Now it works! However, my controller will still be hit if I call ~/Controller or ~/Internal/Controller.

MVC must look at the namespace to determine routing, which I think is unfortunate.

So, if you're having an issue with a controller you moved to an area, make sure you have the correct namespace!

Tuesday, October 14, 2014

Array and Model to MVC Controller Method via jQuery Post()

Posting this as a reminder on how to make the jQuery post() method work with MVC when passing an array.


Monday, September 15, 2014

Google OAuth 2 - The Hard Way

So... Google has deprecated their old single signon code. Unfortunately I'd added that code to 3 different applications. So, we need to move forward!


I tried using the Microsoft WebPages OAuth Library, but the Google provider wouldn't work properly. Tried following the directions here

http://www.beabigrockstar.com/blog/google-signin-for-asp-net-mvc/

... but I was too stupid to "get it" and make it work. Getting weird errors on Google's end. I was already on 2 urgent projects, and now I have to make our Google login work again. It's just web requests, right? I can do some requests, no biggie. Found this site...

https://developers.google.com/oauthplayground/

which is everything the Google OAuth documentation isn't. Gave me everything I needed! Used a lot of code from Jerrie Peelser's blog (http://www.beabigrockstar.com/) which was a huge help. I'm thinking maybe the permissions from the Google dev site just hadn't propagated. Regardless, I created a helper class that just "does it".

You can modify it to suit your needs.

The "GetLoginRedirectUrl" code can be modified to specify which API's you want to access. The "GetAuthenticatedUserEmail" method is just a stupid simple method I use to get the email address. You can request any API that you put in the "GetAuthenticatedUserEmail" method no problem.

I hope you find this useful!

Tuesday, July 22, 2014

Cheesie simple modal dialog

From time to time I have to work on projects where I have old versions of libraries, like jQuery, and I can't upgrade them. Yet, I need some features of libraries I like to use, like Bootstrap. This time, I needed a simple wait dialog that would show up, then go away when I needed it to. I'm working with jQuery 1.4.4 and jQueryUI is just too much for what I'm doing.

So, I found a great article on the web for a simple CSS positioned modal dialog.

http://blog.raventools.com/create-a-modal-dialog-using-css-and-javascript/

And my version is below. All I really changed is some styling, added a variable for content, which I'm setting statically, and have the styles inline instead of in a separate style sheet. This is a quick and dirty fix for a dying project. I've done a better version with bootstrap/angular but that's a different post :)


Monday, June 30, 2014

MVC - Saving Files AND Json Model

When posting files in MVC using AJAX, Microsoft gives us a great tool, the Request.Files collection. However, when passing files via ajax, MVC ignores your json model that you pass as well. You can see the way around this in the code snippets below.



And in our javascript, using jQuery's ajax() method...




I add the json model to the FormData collection. Once that's done, I can pull it from the Request.Form[] Name Value collection when it posts to the server.

WebApi and OData - Without Entity Framework

OData is pretty nice, all said. Makes displaying data in a grid way easier. Microsoft has done some pretty cool things with WebAPI, gotten rid of the last bits of weirdness that remain in MVC (imo). However, their odata implementation adds more typical MS weirdness...

While most oData parameters just "work" and apply directly to your IQueryable, unfortunately $inlinecount isn't one of them.
Fortunately there's a pretty easy workaround.

 Add WebApi and oData using NuGet.
Do this to your method.


The key to this is returning the OData PageResult. It's pretty neat that MS' oData implementation will just work on your IQueryable, without modification. It's just that WebAPI won't return a PageResult when it sees the $inlinecount parameter, like it will when you use an oData controller. My issue with the oData controller is that it forces you into exposing your entire model for each object in your graph. I'm not trying to expose all of my data, I'm just trying to get an easy oData endpoint to feed my sites. Rarely do I need to expose my entire object graph to the user, and those queries are way too heavy. I also don't want a lot of JS baggage by filtering and selecting only the properties I need. This solution works much better for me.

Tuesday, June 17, 2014

File and JSON Post in MVC

It seems strange to me that MVC doesn't handle posting a file and a model at the same time from AJAX by default. It'd be great if you could just send your data and "poof" it's all there for you. It doesn't seem to be, so I'll put down what I've found that works for me.

You create a javascript FormData object. Add your model (with property "model") and your file('s). Post as normal.

In your action method, remove any model parameter you had. You won't need it. Just look at the Request.Form["model"] and deserialize your json. Get your files in the Request.Form collection.

Tuesday, September 10, 2013

MVC 4 Project File Structure

I've been trying to think of a clean way to structure web app folders that keeps things simple and removes clutter.Typically in most web apps, all scripts are thrown willy-nilly into the "Scripts" folder. You end up with tons of js files with no real structure to them. And, think about it... How often do you modify the jQuery source? How about Bootstrap? Modernizr?

Right... Why have them in the most visible place in the folder? The files you modify regularly should be easy to get to. The files that are never modified should be tucked away, only changed when upgrading or removing. We want the most often modified files visible and easily navigated. I came up with the following.

Generic Folder Structure


  • common
    • js
      • jQuery
      • knockout
      • bootstrap
    • css
      • jQuery
      • knockout
      • bootstrap
    • images
      • Any file structure that makes sense. Most image resources for js libraries are contained in their respective folders inside of the css folder
    • fx (or framework)
      • app.js (global settings specific to this application)
      • fx.js (common functions that are a part of the company framework code)
      • Any other files that are specific to the company and are used across the site.
  • views
    • account
      • login.html
      • login.js
    • hearing
      • hearingSummary.html
      • hearingSummary.js
      • hearingResult.html
      • hearingResult.js
      • hearings.html
      • hearings.js
    • common
      • any control templates that are used elsewhere in the application.
  • index.html
  • index.js


Which works well for a typical non-MS website... However, with MVC, it's not standard practice to have the js for a view/partial in the folder where it resides. Also, you can install and upgrade client libraries using NuGet. Unfortunately you don't get a choice as to where these files are installed. So, you're stuck with a "Content" and "Scripts" folder, and unless you modify your routing, you can't put script files in view folders.

To try and keep upgrade functionality but remove clutter, I came up with this compromise...


  • [] Content
    • [] App
      • [] img
        • any images specific to the application
      • site.css
    • [] kendo
    • [] font
  • [] Controllers
  • [] Models
  • [] Scripts
    • [] App
      • fx.js
      • util.js
      • etc...
    • [] kendo
    • bootstrap.js
    • jquery.js
    • knockout.js
    • etc...
  • [] Views
  • [] ViewScripts
    • [] Blog
      • index.js
      • editBlog.js
      • newBlog.js
    • [] Shared
      • layout.js
      • somePartial.js
      • anotherPartial.js
      • etc...
    • [] Security
      • login.js
      • users.js
      • etc...

The key features here are the Content/App folder, the Scripts/App folder, and the ViewScripts folder. By having special subfolders in Content and Scripts, I get away from having files that I modify from being lost in the files I install through NuGet. Typically these files are modified infrequently anyway, but at least this way they're easy to find. How many times have you found derelict css files lost in the Content folder mess? This helps with that.

The biggest benefit comes from moving view specific files to the ViewScripts folder. This folder follows the directory structure of our Views folder. For views/partials that need a specific javascript folder, you merely create one in the ViewScripts folder. Easy to find, easy to navigate. I'm sure there are a million ways to lay out the directory structure of an MVC app. This one works for me. I'm mainly posting this as a personal reference :)

Wednesday, August 21, 2013

Javascript Phone Number Knockout Binding

Hey, all...

Over the years, I must have written a dozen phone number textboxes. Unfortunately I never save the code. I get to play with Knockoutjs at my new job so I figured I'd give the number textbox another shot.



    ko.bindingHandlers.phoneNumber = {
        formatNumber: function (box) {
            var box = $(box);
            var value = box.data("value");
            if (value.length <= 10) {
                value = value.replace(/(\d\d\d)(\d\d\d)(\d\d\d\d)/, '($1) $2-$3');
            } else {
                value = box.data("value").replace(/(\d\d\d)(\d\d\d)(\d\d\d\d)(\d+)/, '($1) $2-$3 x$4');
            }
            $(box).val(value);
        },
        init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
            var _this = ko.bindingHandlers.phoneNumber;
            var textbox = $(element);
            //function formatNumber(box) {
            //}
            textbox.keydown(function (e) {
                // allow cut/copy/paste
                if (e.ctrlKey && (e.which === 67 || e.which === 86|| e.which === 88)) {
                    return;
                }
                // allow delete and backspace
                if (e.which === 8 || e.which === 46) {
                    return;
                }
                // Ensure that a number was pushed
                if (e.shiftKey || !((e.which >= 48 && e.which <= 57) || (e.which >= 96 && e.which <= 105))) {
                    e.preventDefault();
                }
            });
            textbox.focusin(function (e) {
                var value = textbox.data("value") || "";
                textbox.val(value);
            });
            textbox.focusout(function (e) {
                var value = textbox.val().replace(/[^0-9]+/g, '');
                textbox.data("value", value);
                _this.formatNumber(this);
                valueAccessor()(value);
            });
        },
        update: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
            var _this = ko.bindingHandlers.phoneNumber;
            var textbox = $(element);
            var value = valueAccessor()().replace(/[^0-9]+/g, '');
            textbox.data("value", value);
            _this.formatNumber(textbox);

        }
    }

Tuesday, March 19, 2013

Paychex and Horrible Web Applications

Our company recently chose Paychex as our HR portal, benefits management and payroll company. I've gotten paystubs from Paychex before, and thought our move might be a good one. Was I ever wrong.

To view my HR tools, like W2 info, paystubs, Direct Deposit I go to one site. This site takes a username, password and a company ID.

It uses ASP.Net, probably 2.0. When I view the page in Chrome, everything is disabled by default. I have to refresh the page in order to interact with it. The entire site is riddled with slow postbacks, often when choosing a dropdown (instead of a less obtrusive update panel). Despite its looks and poor performance, most of the site actually works, just keep clicking. You might have to click places that make no sense. Some of this is easier in IE, but not much.

To view and enter time and request time off, you go to another site. This site takes a username, a password, and a different company ID. I'm sure this is done for security. (wat).

This site has a completely different look and feel, yet done in that ever-so-quaint ASP.Net 2.0 fashion, again using whole page postbacks to give it that "web 1.1" feel. Navigation here is atrocious. You can "clock in" on the first page, but as salary, I want to manage my time all in one shot. (Don't get me started on why we're having to report our time for each day). There's a different section where you can do this. It uses what looks like a pageable repeater. No styling on it. Just dragged and dropped into the editor, there ya go.

Well, you can select a time type from a dropdown. Of course, changing this dropdown causes a full page postback.  Once you enter time and submit, you can't change it. And, of course there is no validation (server or client) so entering 80 hours for one day is perfectly fine.

There's ANOTHER section you have to go to in order to edit time. And  you don't "edit" it, you delete it for that day and re-enter.

Well, Paychex has created a "revolutionary" "new" way to manage this! MyPaychex.com! This has a more streamlined look. If you look past the javascript error dialogs that show in IE as soon as you log in.

Trying to register is a complete pain, as changing options forces a postback that of course clears any text you entered before the postback happened.

You have to bind the other accounts you have with this one, as all this site is, is a tab container that holds iframes to the other websites.

Yeah... So Paychex... IMO you need to redo your web infrastructure. And be willing to pay for new technology done right.