Very bootstrappy as usual. Reference the directive on the view thus
<error-display display="vm.Error" />
Happy days – a lovely reusable error panel.
The Problem
Sadly it isn’t to be. The previously functional page becomes entirely blank after the directive.
Unhelpfully there is no errors in the console and viewing the HTML source does nothing for us since it is a single page application.
The Solution
The problem is the use of self closing tags when referencing the directive. Referencing the directive with an explicit closed tag resolves the issue. Example – use this markup
Relief all round. Fortunately I found the solution pretty quickly (though I still continued to ignore the daughter). It’s the sort of error that can wasted hours if it hits on a Friday afternoon when you are feeling generally depleted.
Actually the use of self closing tags isn’t that helpful in HTML5. I’ve got into the habit of using them but looking into it more deeply it’s actually a bad habit. They are valid but optional in HTML5 for void elements but invalid for non-void elements. Since they are either optional or invalid its best to avoid them and write out the tag – particularly as it causes obscure issues like this.
I’ve often had the need to populate database tables with chunks of random data for testing and so forth and this random data generator stored procedure does the trick for me. I’ve had it knocking round in my sock draw for quite a while so I’ve dusted it off and updated it. It actually does a bit more than I remembered. I’ve used it on SQL Server 2005 and 2008 and the INFORMATION_SCHEMA use should make it forwardly compatible.
Code
usp_RandomInsertGenerator
This procedure actually does the heavy lifting and generates the insert statements.
Create Procedure usp_RandomInsertGenerator
(
@TableName nvarchar(128)
)
As
-- does foreign keys
-- max character length
-- standard data types
-- identity columns
-- detects nullable columns and leaves as null
-- does not do
-- omitted datatypes - binary, varbinary, image, timestamp
-- Hacks
-- foreign key links where linked to child table via a unique index rather than a primary key. Assumpts FK of 1
-- assumes
-- foreign keys are of a numeric type i.e. ints, tinyints, floats etc...
-- assumes foreign key constraints exist
Begin
Set NoCount On
Declare @ColumnName nvarchar(128)
Declare @FK_TableName nvarchar(128)
Declare @FK_ColumnName nvarchar(128)
Declare @ConstraintName nvarchar(128)
Declare @DataType nvarchar(128)
Declare @CharacterMaximumLength int
Declare @Sql nvarchar(max)
Declare @MaxValue int
Declare @InsertValue nvarchar(400)
Declare @SqlOutputFields nvarchar(max)
Declare @SqlOutputValues nvarchar(max)
Declare @FirstLoop bit
Declare @IsIdentity bit
Declare @StringInsert bit
Declare @DummyText varchar(48)
Set @FirstLoop = 1
Set @SqlOutputFields = 'INSERT INTO ' + @TableName + ' ('
Set @SqlOutputValues = ' VALUES ('
Set @DummyText = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
-- cursor loops through every column for named table
Declare procCursor CURSOR FORWARD_ONLY
For
Select
COLUMN_NAME,
DATA_TYPE,
CHARACTER_MAXIMUM_LENGTH
From
INFORMATION_SCHEMA.COLUMNS col
Where
TABLE_NAME = @TableName
And IS_NULLABLE = 'NO'
Order By
ORDINAL_POSITION
Open procCursor
Fetch Next From procCursor Into @ColumnName, @DataType, @CharacterMaximumLength
while @@fetch_status <> -1
Begin
-- datatypes i have not bothered implementing
if @DataType = 'cursor' OR @DataType = 'timestamp'
Or @DataType = 'binary' Or @DataType = 'varbinary' Or @DataType = 'image'
Begin
Raiserror('Unsupported Data Type', 1, 16)
End
--reset variables
Set @FK_TableName = ''
Set @FK_ColumnName = ''
Set @StringInsert = 0
Set @ConstraintName = ''
-- Do not add in an insert value if the loop is an identity
Select @IsIdentity = COLUMNPROPERTY(OBJECT_ID(@TableName),@ColumnName,'IsIdentity')
if @IsIdentity = 1
Begin
Fetch Next From procCursor Into @ColumnName, @DataType, @CharacterMaximumLength
continue
End
-- getting the value to be inserted for this data type
if @DataType = 'varchar' Or @DataType = 'nvarchar'
Or @DataType = 'char' Or @DataType = 'nchar'
Or @DataType = 'text' Or @DataType = 'ntext'
Begin
Set @StringInsert = 1
Set @InsertValue = SubString(@DummyText, 1, @CharacterMaximumLength)
End
Else if @DataType = 'datetime' Or @DataType = 'smalldatetime'
Begin
Set @StringInsert = 1
Set @InsertValue = Cast(GetDate() as varchar(20))
End
Else if @DataType = 'uniqueidentifier'
Begin
Set @StringInsert = 1
Set @InsertValue = Cast(NewId() as varchar(200))
End
Else -- it is some key of numeric type
Begin
-- getting the child table indexes
Set @Sql = '
Select
@FK_TableName = pkconst.TABLE_NAME,
@FK_ColumnName = pkconst.COLUMN_NAME,
@ConstraintName = coluse.CONSTRAINT_NAME
From
INFORMATION_SCHEMA.KEY_COLUMN_USAGE coluse
Join INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS fkconst
On fkconst.CONSTRAINT_NAME = coluse.CONSTRAINT_NAME
Left Join INFORMATION_SCHEMA.KEY_COLUMN_USAGE pkconst
On pkconst.CONSTRAINT_NAME = fkconst.UNIQUE_CONSTRAINT_NAME
Where
coluse.TABLE_NAME = @TableName
And coluse.COLUMN_NAME = @ColumnName'
Execute sp_executesql @Sql, N'@TableName nvarchar(128),
@ColumnName nvarchar(128),
@FK_TableName nvarchar(128) OUTPUT,
@FK_ColumnName nvarchar(128) OUTPUT,
@ConstraintName nvarchar(128) OUTPUT',
@TableName=@TableName,
@ColumnName=@ColumnName,
@FK_TableName=@FK_TableName OUTPUT,
@FK_ColumnName=@FK_ColumnName OUTPUT,
@ConstraintName=@ConstraintName OUTPUT
if Len(@FK_TableName) > 0 And Len(@FK_ColumnName) > 0
Begin
/* have found foreign key and the lookup table
so pick a random primary key from the lookup table */
Set @Sql = 'Select top 1 @InsertValue = Cast(' + @FK_ColumnName +
' as varchar(200)) From '
+ @FK_TableName + ' Order By newid()'
Execute sp_executesql @Sql, N'@InsertValue nvarchar(128) OUTPUT', @InsertValue=@InsertValue OUTPUT
End
Else if(Len(@ConstraintName) > 0)
Begin
/* OK we've found the foreign key constraint but have no idea what the
lookup table is. This is because we are joining on a unique index to the
lookup table not a primary key. Make a MASSIVE assumption in this instance
- that the lookup table has a link value of 1 */
Set @InsertValue = '1'
End
Else
Begin
-- no foreign key so the max that can be inserted is based on the datatype
-- do not bother with any thing greater than thirty thousand - big enough
if @DataType = 'bit'
Set @MaxValue = 1
else if @DataType = 'tinyint'
Set @MaxValue = 255
else if @DataType = 'smallint'
Set @MaxValue = 32767
else
Set @MaxValue = 32767
-- randomly generate a number to insert up to maximum
Set @InsertValue = Cast(ROUND(((@MaxValue - 1) * RAND() + 1), 0) as varchar(200))
End
End -- end of numeric processing
-- building up output string
Declare @Delimiter char(1)
if @FirstLoop = 1
Begin
Set @FirstLoop = 0
Set @Delimiter = ''
End
Else
Begin
Set @Delimiter = ','
End
Set @SqlOutputFields = @SqlOutputFields + @Delimiter + @ColumnName
if @StringInsert = 1
Begin
Set @SqlOutputValues = @SqlOutputValues + @Delimiter + '''' + @InsertValue + ''''
End
Else
Begin
Set @SqlOutputValues = @SqlOutputValues + @Delimiter + @InsertValue
End
Fetch Next From procCursor Into @ColumnName, @DataType, @CharacterMaximumLength
End -- finished this column = go to next
close procCursor
deallocate procCursor
-- outputting the sql string
Set @SqlOutputFields = @SqlOutputFields + ')'
Set @SqlOutputValues = @SqlOutputValues + ')'
select @SqlOutputFields + ' ' + @SqlOutputValues
End
Go
usp_RandomInsertGeneratorWrapper
It’s very easy to create a wrapper stored procedure to get multiple statements. One row of random data isn’t that much use – 1000 might be.
Create Procedure usp_RandomInsertGeneratorWrapper
(
@TableName nvarchar(128),
@NumberOfInserts int
)
As
Begin
Set NoCount On
Declare @StatementCount int
Set @StatementCount = 0
While @StatementCount < @NumberOfInserts
Begin
exec usp_RandomInsertGenerator @TableName
Set @StatementCount = @StatementCount + 1
End
End
Usage
To get an insert for one line of data
exec usp_RandomInsertGenerator ‘MyTableName’
To get multiple insert statements call the wrapper function
It outputs an insert T-SQL statement for any named table in the database.
Detects foreign keys and inserts and makes a random but valid link to the child table (this was the hard part)
Detect different data types and does the necessary SQL formatting
Accounts for max character length in string type fields
Accounts for identity columns
Detects nullable columns and leaves them null
What it doesn’t do
Doesn’t account for unique indexes
There are omitted datatypes (cursor, timestamp, binary, varbinary, image) that I’m not really interested in right now.
Hacks and shortcuts
Where the foreign key links are linked to a child table via a unique index rather than a primary key it assumes a foreign key of 1. The INFORMATION_SCHEMA views don’t include index information. So this would necessitate delving into the sys.index table. Not impossible but the proc was complicated enough as it was. I didn’t need it so I didn’t do it.
Assumptions
Foreign keys are of a numeric type i.e. int, float et. al.
Foreign key constraints exist on the database. It isn’t magic – it can’t work out the relationships if they don’t exist.
Room for improvement
Here’s some extensions that may be useful for individual projects
Implement missing datatypes.
Create custom inserts for known field names to give more realistic data for your application
Randomise the character data properly.
So that’s it. Live it, love it and fiddle about with it a bit.
As ever the source code is on GitHub. There is nothing more than what is here but my WordPress code formatting plugin hasn’t made a great job of formatting the SQL code so the GitHub link might give clearer view of the SQL. Also, it seemed cruel not to give it a home on my demo repository with the rest of my code samples. https://github.com/timbrownls20/Demo/blob/master/T-SQL/usp_RandomInsertGenerator.sql
A quick one. Most web developers can write JavaScript and I suspect it’s on most developer’s CVs somewhere. But it’s easy to write bad JavaScript and not so easy to write the good stuff. I guess it’s because JavaScript itself is so free and easy. It isn’t opinionated.
So I thought this JavaScript best practice primer was an excellent read and it’s freely available. It’s an online bonus appendix to Simon Holme’s Getting MEAN book. The book itself is really good and worth a read if you are interested in the MEAN stack. The appendix though is like multi vitamins – it’s good for everyone.
Even though I’ve been writing JavaScript on and off for years it still showed me a thing or two. It covers things like
Variable hoisting
Block and global scope
Callbacks
Comparison operators
Object orientated coding including IIFE
And more ….
Only takes half an hour or so to read and I’m a slow reader. One to recommend to family and friends. It’s certainly what my Mum is getting next Christmas.
I am aware that there are many many other ways to get debug information from an AngularJS application but that’s not stopped me implementing another one. I do actually find a customised output of JSON objects useful particularly when working with deep object graphs. The implementation also is useful as a worked example of a couple of AngularJS principles.
The Demo Application
The demo application is a bookshelf application that searches the google book API and enables the user to add books to their own library. This is my ‘go to’ demo application that I’ve implemented a couple of times in different frameworks. This one is using the MEAN stack and the source code can be found here.
The Problem
When I search the google API I get a very large complex JSON object as the output. I want to know what is in there as I’m saving bits of it in my own database and might be interested in other bits. I want it displayed but easy to toggle on and off. I’m ultimately going to read something from the query string to do this as it is easy to toggle with minimal fuss.
When it’s finished it’s going to look like this…
Google Bool API results displayed in debug panel
I’m going to use bootstrap for styling throughout as I’m not hugely concerned about how it looks – it’s just got to be reasonably neat. So its bootstrap classes throughout I’m afraid.
The Solution
The solution is going to demonstrate the following
The use of directives in AngularJS
Using global values and config
Setting globally accessible values from the query string.
Step 1: Implement a debug panel
The initial implementation is directly on the search page. When I do a search I want to see the following debug output
Debug panel displayed on book search page
Before I do a search I don’t want to see anything.
The implementation is
<!— book details page -->
<div class="row" ng-show=" searchResults ">
<div class="col-md-8">
<hr />
<pre>{{searchResults | json}}</pre>
</div>
</div>
<!— rest of book details page -->
The scope has a property SearchResults with a giant json object direct from the GoogleAPI. I want to show this. I use the json filter to prettify the result and it’s wrapped in pre tags so it displays like code. The ng-show attribute hides it if the searchResults is null or generally falsy which is what I want. That’s pretty good and works for me.
Step 2: Make it a directive so it can be reused
Now I want this on every page and I want it to display anything I want not just the searchResults. I want it to be generally useful. To achieve this I’m going to use directives which allow me to include fragments of HTML across different parts of my site. I now want it on the book details page as well.
The first job is to extract out the debug panel into a separate html file and to remove the dependency on searchResults. The debug panel html is extracted and saved into its own file at
This is the contents of the entire file now. I’ve removed the binding to searchResults and I have now got a property called ‘display’. This is any JSON object I want to display in my results panel.
Next job is to tell my angular application about my new directive. So I create a new JavaScript file and put this in
This does a few things. It tells angular where to find the html fragment for display via the templateUrl property. More interesting it also uses scope isolation to feed in the JSON object I want to display.
scope:
{
display : "=display"
},
The scope property is the link between the page and the directive. I can insert my parent page scope into an attribute called display and this becomes available to the directive. It breaks the hardcoded dependency on the parent scope and makes it all more flexible. The ‘=’ in ‘=display’ tells angular it is an attribute.
That’s probably not hugely clear but finishing the example should help us. The last step is to reference the directive on the book details page. I need to remember to include the script
Then the directive is referenced as markup which looks quite neat
<debug-display display="vm.book"></debug-display>
So the display attribute is bound to the book property which is then fed through to the debug panel and displayed. I can put anything into it not just searchResults as before.
Debug panel displayed on book details page
I can now go back to the search page and implement the directive in the same way.
And that will display my search results in prettified json which I quite like. It’s even better now and I am mentally slapping myself on the back. However I’m not finished. I don’t always want to see the debug panel. Sometimes I just want to see the page as it will be when it goes live with no debug information at all. I want to be able to toggle it on and off with ease.
Step 3: Using a global setting to toggle the debug panel
I have ambitions for my BookShelf demo site to be truly huge; a site which will make eBay look small. To that end I don’t want to have to turn my debug panel on and off manually everywhere it is used. I want one point of control.
I’m going to set up a DebugOn flag when the application initialises. For this I could use a global constant like so
app.constant('DebugOn', 1);
However (spoiler alert) I’m going to want to amend them later on so a global value is more the way to go. I’m setting this when the app initialises so it will be
(function () {
'use strict';
var app = angular.module('app', ['ngRoute', 'infinite-scroll']);
// routing and other set up
app.value("DebugOn", 1);
})();
I can use the built in dependency injection to access the global variable in any service, filter etc.. so I’m going to use that to grab it in the directive script …
The directive has become a bit more complex but it’s not too terrifying. I’m taking the DebugOn value as a DI injected parameter – very standard Angular stuff. I want to hide the debug panel if it is set to 0. In essence I want to change the DOM if the DebugOn variable is 1 and the link property enables us to do that.
The elem parameter is a collection of all the directive element outputs in the app at that time. For the debug panel it will be one but I will be good and iterate through it anyway – we just obliterate the innerText and thus hide them from the user.
Now we have done this turning the debugging panel off becomes a simple matter of amending the value of DebugOn variable in one place and restarting the application.
app.value("DebugOn", 1);
Just what I wanted.
Step 4: Using the query string to toggle the debug panel
But it’s not enough. It never is. I’m not satisfied with amending the application – I want to just pop something on the query string, have my application pop into debug mode and have my debug panels spring into life like desert flowers in the rain. I want it and I can have it.
http://myapplication#angularstuff?DebugOn=1
Should turn on the debugger but as I’m navigating around my single page application my querystring is going to disappear. I could maintain it but I don’t want to – it seems like hard work. So I’m just going fall back to some good old fashioned JavaScript. The full url is available in
window.location.href;
So I’m going to use a couple of functions to grab the value and put it in the global variable
function getDebugParam(){
var debugParam = getParameterByName("Debug");
if(!debugParam) debugParam = 0;
return debugParam;
}
function getParameterByName(name, url) {
if (!url) {
url = window.location.href;
}
name = name.replace(/[\[\]]/g, "\\$&");
var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
results = regex.exec(url);
if (!results) return null;
if (!results[2]) return '';
return decodeURIComponent(results[2].replace(/\+/g, " "));
}
And the setting of the debug variable becomes
app.value("DebugOn", getDebugParam());
And done. I done even need to change my app to get the debug panels on. A quick fiddle with my querystring and there they are. Lovely.
Further Development
As much as I like the query string idea I wouldn’t want to see it in production. As an immediate project I would want to disable this in prod environments. It’s not hard but needs to be done. I’ve got other ideas around multiple panels, debug levels and improved display but I’ll leave those for another day.
That’s it – end of post other than notes. Happy New Year everyone for 2017.
Full Script
There is a bunch of code here so to aid the discerning coder – this is the all code needed for the debug panels
function getDebugParam(){
var debugParam = getParameterByName("Debug");
if(!debugParam) debugParam = 0;
return debugParam;
}
function getParameterByName(name, url) {
if (!url) {
url = window.location.href;
}
name = name.replace(/[\[\]]/g, "\\$&");
var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
results = regex.exec(url);
if (!results) return null;
if (!results[2]) return '';
return decodeURIComponent(results[2].replace(/\+/g, " "));
}
app.module.js
(function () {
'use strict';
var app = angular.module('app', ['ngRoute']);
//.. other setup such as routing
app.value("DebugOn", getDebugParam());
})();
Note about Immediately Invoked Function Expressions
I haven’t wrapped all my examples in an IIFE. This is just to save space and reduce nesting in the examples. Just mentally put them back in if they are absent i.e.