20+ year software development veteran blogging about my journey & experiences learning the latest web development technologies.
Don't wanna be here? Send us removal request.
Text
VueJS & Firestore Stripped-Back - Tutorial Part 6
Sorting Data
We’ve completed the CRUD elements of our app however there’s lots more to cover in building a useful app and we’ll break down the various subjects into short, individual parts. In this part we’ll implement a feature to enable the user to sort their data by any column they want.
Just in case you haven’t been through the previous parts of this tutorial, the links are given below :
1) Getting Started 2) Create 3) Read 4) Update 5) Delete
Our sorting feature will let the user click on a column header in the table to sort by that column. We’ll first of all add a new variable to the data object in our Vue Instance called sortBy. We’ll set it to our default sort order, lastName :
data : { appTitle : ‘EmployeeMagic’, mainStyle : { ‘margin’ : ’20px’ }, tableStyle : {’margin-top’ : ‘20px’}, udButton : {’float’ : ‘right’}, employee : {firstName : ‘’, lastName : ‘’, jobTitle : ‘’, dept : ‘’, age : 0, id : ‘’ }, db : {}, employees : [ ], sortBy : ‘lastName‘ }
The column headers are held within the <th> tags on our <table> element in our template. To enable the user to select which column to sort by, we need to add click handlers to these <th> tags. We’ll call the same click handler method for each header and simply pass in the relevant field name for each column.
<table class=“table table-bordered table-striped”> <thead> <th v-on:click=“changeSortBy(’lastName’)>Name</th> <th v-on:click=“changeSortBy(’jobTitle’)>Job Title</th> <th v-on:click=“changeSortBy(’dept’)>Department</th> <th v-on:click=“changeSortBy(’age’)>Age</th> </thead>
We need to add our click handler method, changeSortBy, to the methods object on our Vue Instance. We’ll add it as the last method underneath the clearEmployee method.
changeSortBy : (sortByColumn) => { app.sortBy = sortByColumn }
We set our sortBy variable to the same value as that passed in by the column that was clicked on. Now we need to implement the code to act on the sort request. It’s important to note that we’ll be sorting the data by calling on Firestore to do the work for us, so that we ask for the data to be delivered to us from our database ready sorted. We could also simply sort the array that we already have in memory however the purpose of our tutorial is to explain how to put the features of Vue and Firestore into action and not the details of how to sort an array in Javascript (I may cover that in a short post in the future). At the moment we have a listener on our Employee collection using Firestore’s onSnapshot() method and passing in a callback function to define how to react to changes in the data. However, if you remember, when defining our query we determine our sort order before implementing our listener as shown below :
let query = that.db.collection(’Employees’).orderBy(’lastName’) query.onSnapshot((snapshot) => {
In order to define a new query and get our data using a new call to onSnapshot() we will first need to stop, or rather unsubscribe from, our current listener. As you would expect, Firestore gives us a fairly simple way to do this. As we’ve already covered, onSnapshot is an exception to the rule when it comes to Firestore APIs as it doesn’t return a Promise. Instead it returns a function that, when run, will unsubscribe from the listener that defined it. In other words when we run a query and subscribe to a listener, the listener itself gives us the means to which we can unsubscribe from it.
To implement this we’ll need to grab the returned unsubscribe function from the onSnapshot function call (which is presently located in the created lifecycle hook) into our data object so we can access it elsewhere when needed eg when the user changes the sort order.
data : { appTitle : ‘EmployeeMagic’, mainStyle : { ‘margin’ : ’20px’ }, tableStyle : {’margin-top’ : ‘20px’}, udButton : {’float’ : ‘right’}, employee : {firstName : ‘’, lastName : ‘’, jobTitle : ‘’, dept : ‘’, age : 0, id : ‘’ }, db : {}, employees : [ ], sortBy : ‘lastName‘, unsubscribe : {} }
We now need to change our call to onSnapshot in our created function
that.unsubscribe = query.onSnapshot((snapshot) => {
Going back to our column header click handler, we need to now make a call to the unsubscribe() function to stop the current listener.
changeSortBy : (sortByColumn) => { app.sortBy = sortByColumn app.unsubscribe(); }
We can now look at how we can run a new query based on the new sort order defined by the user. The query we’ve got presently already does the majority of the work that we need however the code for it is defined on our created hook function which can only be run once - when our Vue Instance is first created. We’ll therefore need to take that code out of the created function and into a new method which we’ll call dbSubscribe and place it at the bottom of our methods object. Note: We’ll be calling dbSubscribe from our created function (so data loads on start-up). created is not an arrow function (Vue advises against defining lifecycle hook functions as arrow functions) and therefore anything we call from created is going to trip over the this issue again and so in the context of being called from a standard function, we’re not going to be able to reference our Vue Instance by using either this or app from within our dbSubscribe method. Therefore we’ll receive the Vue Instance as a parameter and assign it to that and if we don’t receive the parameter (eg it got called from elsewhere), we’ll assign app to that. The remainder of the code can stay the same.
dbSubscribe : (vi) => { let that = vi if (! that) that = app let query = that.db.collection(’Employees’).orderBy(’lastName’) that.unsubscribe = query.onSnapshot((snapshot) => { that.employees = [ ] snapshot.forEach((doc) => { that.employees.push({ firstName : doc.data().firstName, lastName : doc.data().lastName, jobTitle : doc.data().jobTitle, dept : doc.data().dept, age : doc.data().age }) }) }
We can now remove the query code from our created function and simply call the dbSubscribe() method passing in this as the parameter as explained above. We will also remove defining that, as it’s no longer needed, and replace any reference to it back to this.
created : function() { this.db = firebase.firestore() this.dbSubscribe(this); }
Back to our changeSortBy click handler method we can now make a call to the dbSubscribe function to re-run the query.
changeSortBy : (sortByColumn) => { app.sortBy = sortByColumn app.unsubscribe(); app.dbSubscribe(); }
If you save and refresh in your browser you can now click on the column headers but still nothing seems to be happening. However everything we need is in place, we just need to order by the chosen column in our query. To do this we’ll go back to our dbSubscribe method and instead of hard-coding the parameter that we pass into orderBy() we’ll pass in the sortBy variable which is set by the user when they click a different column header (we’ll also put a check to ensure a sort order is passed otherwise we won’t sort by anything).
dbSubscribe : (vi) => { let query let that = vi if (! that) that = app if (that.sortBy) query = that.db.collection(’Employees’).orderBy(that.sortBy) else query = that.db.collection(’Employees’) that.unsubscribe = query.onSnapshot((snapshot) => { that.employees = [ ] snapshot.forEach((doc) => { that.employees.push({ firstName : doc.data().firstName, lastName : doc.data().lastName, jobTitle : doc.data().jobTitle, dept : doc.data().dept, age : doc.data().age }) }) }
Give that a go in your page and you should now see that your list of employees is sorted as you select. Before we finish, there’s just one last touch we should add to this feature. At the moment it’s not at all clear which column the list is currently sorted by and it’d be good UI to show this to the user. We can do this very simply by showing a star next to the name of the column that sorted. Let’s go back to the <th> tags on our table and add this in using a <span>.
<table class=“table table-bordered table-striped”> <thead> <th v-on:click=“changeSortBy(’lastName’)>Name <span>*</span></th> <th v-on:click=“changeSortBy(’jobTitle’)>Job Title <span>*</span></th> <th v-on:click=“changeSortBy(’dept’)>Department <span>*</span></th> <th v-on:click=“changeSortBy(’age’)>Age <span>*</span></th> </thead>
Great but we only want to display the star for the column that’s currently sorted. From what we’ve covered previously which Vue directive do you think we can use to achieve this? You got it!! Well done! That’s right, it’s the v-if directive. If we apply a v-if directive to the <span> on each header we can pass a condition to check if that column header is for the currently sorted column.
<table class=“table table-bordered table-striped”> <thead> <th v-on:click=“changeSortBy(’lastName’)>Name <span v-if=“sortBy === ‘lastName’”>*</span></th> <th v-on:click=“changeSortBy(’jobTitle’)>Job Title <span v-if=“sortBy === ‘jobTitle’”>*</span></th> <th v-on:click=“changeSortBy(’dept’)>Department <span v-if=“sortBy === ‘dept’”>*</span></th> <th v-on:click=“changeSortBy(’age’)>Age <span v-if=“sortBy === ‘age’”>*</span></th> </thead>
Save and refresh the page and you’ll be able to click on any of the column headers, sort the list and see a star by the sorted column. Pretty cool eh?
OK you’ve definitely earned a warm, caffeine-based beverage now so we’ll finish things off here. In this part we’ve covered assigning click event handlers to table headers, how to unsubscribe from a Firestore onSnapshot listener, how to sort a list on a query based on the user’s selection and finally how to use v-if to highlight the sorted column,
In the next part of this tutorial we’ll cover how to add a Search feature to our app and how to filter the data we get back from Firestore based on the user’s search. Hope you can make it.
You can download the completed code for this part of the tutorial on Github using the repo below and select the part6 folder. https://github.com/MancDev/VueFire
0 notes
Text
VueJS & Firestore Stripped-Back - Tutorial Part 5
In this tutorial series we are stripping back everything by starting from scratch using just a html page and a code editor to build full-stack web applications with VueJS and Firebase Cloud Firestore and nothing else.
Deleting data
Good to see you again. In this 5th part of our tutorial we’re going to be explaining the D in CRUD - Deleting data. If you haven’t been following along with the CR and U, I’ve put the links to the previous parts below :
1) Getting Started 2) Create 3) Read 4) Update
We did most of the setup work in the previous part on how to Update data so this post will be fairly short in comparison. To Delete data we can simply add a Delete button, next to the Edit button we added in part 4, for each employee listed and then add an event handler to the button to delete the data from Firestore.
First of all let’s add the Delete button to our template.
<td>{{ employee.dept }}</td> <td>{{ employee.age }} <button v-on:click="editEmployee(idx)" class=“badge badge-primary”>Edit</button> <button v-on:click="deleteEmployee(idx)" class=“badge badge-danger”>Delete</button> </td>
Before we write the click handler, if you save and refresh in your browser you will see the two buttons, Edit and Delete, displayed in the Age column. All well and good but it looks a little untidy so we’ll align the buttons over to the right. Again we’ll do the styling using Vue’s v-bind:style directive so we’ll add a new styling variable called udButton to our view model
var app = new Vue({ el : ’#app’, data : { appTitle : ‘EmployeeMagic’, mainStyle : { ‘margin’ : ’20px’ }, tableStyle : {’margin-top’ : ‘20px’}, udButton : {’float’ : ‘right’},
Back in our template we’ll add a div around the two buttons and assign the new style to the div.
<td>{{ employee.age }} <div v-bind:style=“udButton”> <button v-on:click="editEmployee(idx)" class=“badge badge-primary”>Edit</button> <button v-on:click="deleteEmployee(idx)" class=“badge badge-danger”>Delete</button> </div> </td>
Save and refresh now and you’ll see it looks a lot neater. Now let’s look at implementing the deleteEmployee() click handler method. We’ll include it in the methods object of our Vue Instance, just before the clearEmployee() method.
deleteEmployee : (idx) => { }, clearEmployee : () => {
Like the editEmployee method we first of all have to use the index of the selected employee, passed as idx into the method, to retrieve the right employee object and, importantly, it’s id.
let emp = app.employees[idx] if (emp) {
}
We now need to get an object reference to the document in our Employees collection in Firestore. As we did with the saveEmployee method to update an existing employee document, we use the doc() method on the Employees collection and pass in the unique id for the selected employee.
if (emp) { let docRef = app.db.collection('Employees').doc(emp.id) }
Now that we have a reference directly to the document that we want to delete we simply have to call the delete() method.
if (emp) { let docRef = app.db.collection('Employees').doc(emp.id) if (docRef) { docRef.delete() } }
As we’ve covered before, almost all Firestore’s methods are asynchronous so we can pass code that we want to execute after Firestore has confirmed the deletion into the returned promise’s then() method. In this case we’ll simply show the user an alert message to let them know it’s been deleted and write any problems to the console using the promise’s catch().
if (emp) { let docRef = app.db.collection('Employees').doc(emp.id) if (docRef) { docRef.delete() .then(() => { alert(emp.firstName + ' ' + emp.lastName + ' has been deleted!') }) .catch(() => { console.log('Error deleting employee ' + app.employee.firstName + ' ' + app.employee.lastName) }) } }
So let’s put the deleteEmployee method all together
deleteEmployee : (idx) => { let emp = app.employees[idx] if (emp) { let docRef = app.db.collection('Employees').doc(emp.id) if (docRef) { docRef.delete() .then(() => { alert(emp.firstName + ' ' + emp.lastName + ' has been deleted!') }) .catch(() => { console.log('Error deleting employee ' + app.employee.firstName + ' ' + app.employee.lastName) }) } } }
And that’s it! You can go get your deserved hot beverage of choice earlier today! We’ve covered adding another button for Delete to the template and adding a style to make the buttons display a bit neater. We then hooked a method up to the Delete button which goes to the selected document on Firestore and deletes it. We’ve now completed our CRUD operations but we’re a long way from being done with our tutorial. In the next part we’ll take a look at sorting the data in the list to how the user wants. There’s lots more to come in this series so stick with me! See ya next time.
You can download the completed code for this part of the tutorial on Github using the repo below and select the part5 folder. https://github.com/MancDev/VueFire
0 notes
Text
VueJS & Firestore Stripped-Back - Tutorial Part 4
In this tutorial series we are stripping everything back to basics to explain how to build full-stack web applications with VueJS and Firebase Cloud Firestore - no CLIs, NPM or Webpack - just a html page and a code editor.
Updating your data
Welcome back. In this fourth part of our Stripped-Back tutorial we’re going to be explaining the U in CRUD - Updating data. By the way if you haven’t read the previous parts of this tutorial please feel free to do so using these links : Part 1, Part 2, Part 3
In the previous part of the tutorial we retrieved the data from our Firestore database by running a realtime query and populating an employees array with the data we retrieved including all of the fields such as lastName, jobTitle etc. However to be able to update (and indeed delete for that matter) we need to be able to get a handle on a unique ID for each employee document that is retrieved so we’ll know what to update when the user saves a change. If you remember from part 2, when you were checking your Firestore console, we mentioned a strange looking ID attached to each document in the Employees collection - this is what we’ll need to get hold of.
To do this we’ll first need to setup a place to store a unique id, so we’ll add an Id property to our employee object in the data section of our Vue Instance Object :
var app = new Vue({ el : ’#app’, data : { appTitle : ‘EmployeeMagic’, mainStyle : { ‘margin’ : ’20px’ }, employee : {firstName : ‘’, lastName : ‘’, jobTitle : ‘’, dept : ‘’, age : 0, id : ‘’ }, db : {}, employees : [ ] },
Next we’ll need to go back and change our callback function which gets the data from each document and puts it into our employees array. In there we’ll need to make sure the unique id from each document is stored in the employee’s new id property as well as the other fields.
created : function() { let that = this that.db = firebase.firestore() let query = that.db.collection(’Employees’) .orderBy(’lastName’) query.onSnapshot((snapshot) => { that.employees = [ ] snapshot.forEach((doc) => { that.employees.push({ id : doc.id, firstName : doc.data().firstName, lastName : doc.data().lastName, jobTitle : doc.data().jobTitle, dept : doc.data().dept, age : doc.data().age }) }) }) }
Notice we didn’t need to go through the data() method to get our id, it’s available directly on the document object itself. On our template, we’ll add an Edit button into the table alongside each employee so the end-user can click the Edit button next to the employee they want to update. We’ll add the button inside the Age column.
<table class=“table table-bordered table-striped”> <thead> <th>Name</th> <th>Job Title</th> <th>Department</th> <th>Age</th> </thead>
<tbody> <tr v-for=“employee in employees”> <td>{{ employee.firstName }} {{ employee.lastName }}</td> <td>{{ employee.jobTitle }}</td> <td>{{ employee.dept }}</td> <td>{{ employee.age }} <button class="badge badge-primary">Edit</button> </td> </tr> </tbody> </table>
We obviously need to have a click event handler on the new Edit button but each button needs a reference to the employee it represents in order to know which one has been selected to be edited. Vue offers a way to deal with this by allowing a second parameter in the v-for directive. This second parameter can be used to expose the index of the array element that v-for is iterating over so we can reference it elsewhere, in this case we want to pass it as the parameter for our click event handler so the function we call knows the index of the employee it’s dealing with.
<tr v-for=“(employee, idx) in employees”>
We can now assign our click handler to our Edit button and pass in the index of each employee, using the idx that we exposed from v-for, as the parameter. We explained the v-on:click directive in part 2 (remember you can also use the shorthand @click if you’d like).
<button v-on:click=“editEmployee(idx)” class="badge badge-primary">Edit</button>
The next step is to implement the editEmployee() click handler which we can do by adding it to the methods object in our Vue Instance. Remember we’re implementing all of our methods in our Vue Instance as arrow functions.
methods : { saveEmployee : () => { if ((app.employee.firstName) && (app.employee.lastName)) app.db.collection(‘Employees’).add(app.employee) .then(function() { app.clearEmployee() }) .catch(function() { console.log(‘Error saving employee ‘ + app.employee.firstName + ‘ ‘ + app.employee.lastName) }) else alert(‘You must enter both a first and last name to save.’) }, editEmployee : (idx) => { }, clearEmployee : () => { app.employee = { firstName : ‘’, lastName : ‘’, jobTitle : ‘’, dept : ‘’, age : 0 } } },
The job of the editEmployee function is to grab the selected employee’s information and populate the employee object’s properties from the correct element on the employees array. We can determine which employee in our array to use by referencing the idx parameter passed to the function from the button. Once our employee object is populated, Vue’s data binding takes care of the rest to display it on the page.
editEmployee : (idx) => { let emp = app.employees[idx] if (emp) { app.employee = { id : emp.id, firstName : emp.firstName, lastName : emp.lastName, jobTitle : emp.jobTitle, dept : emp.dept, age : emp.age } } },
Save the changes and refresh the browser and make sure everything is working as it should. You should see a blue Edit button in the Age column of the table and when you click it, that employee’s information should be displayed in the input boxes. We now need to handle saving updates to existing records when the user clicks Save rather than simply adding it as new record as it does now. We’ll keep the same Save button and the same saveEmployee click handler method, however we’ll make a decision as to whether we need to save as a new employee or as an update to an existing employee. The way to determine this is quite straightforward, if the employee object has a value in it’s id property it’s an existing record, otherwise it’s a new record. For this to work we need to make a quick change first to the clearEmployee method and ensure it clears the id property as well.
clearEmployee : () => { app.employee = { firstName : ‘’, lastName : ‘’, jobTitle : ‘’, dept : ‘’, age : 0, id : ‘’ } }
Let’s go to our saveEmployee method and add that condition to determine whether we’re saving a new employee or an existing one. For saving new employees we simply need to check if the employee.id isn’t set. Note that rather than simply save the employee object as we did previously, we’re defining a new object from the information in the employee object. The reason for this is simply that we’ve added an id property to the employee object and we don’t want to save this as an additional field on the document
saveEmployee : () => { if ((app.employee.firstName) && (app.employee.lastName)) { let saveEmp = { firstName : employee.firstName, lastName : employee.lastName, jobTitle : employee.jobTitle, dept : employee.dept, age : employee.age } if (! app.employee.id) //check if the id has not been set app.db.collection(‘Employees’).add(saveEmp) .then(() => { app.clearEmployee() }) .catch(() => { console.log(‘Error saving employee ‘ + app.employee.firstName + ‘ ‘ + app.employee.lastName) }) else { } //if the id has been set } else alert(‘You must enter both a first and last name to save.’) },
Now let’s see how to save an update to an existing employee. First of all we need to grab an object reference to the specific document we want to update and we get this using the id of the employee.
let docRef = app.db.collection('Employees').doc(app.employee.id)
Now we’ve got a reference to the specific document in our Employees collection in Firestore, we can just simply call Firestore’s set() method and pass in the object with our updated information and our employee record will be updated.
if (docRef) docRef.set(saveEmp)
The set() method is asynchronous, like most Firestore methods, so if there’s any code we want to execute once we’re certain that the update has saved, we can implement it in the returned promise’s then() method (as we covered in part 2). In the method we pass to then() we simply call clearEmployee to clear the inputs and make it ready to add a new employee again, just as we did when adding new employees (and we’ll add a catch() just in case).
if (docRef) docRef.set(saveEmp) .then(() => { app.clearEmployee() }) .catch(() => { console.log(’Update to ‘ + app.firstName + ' ' + app.lastName + ' did not save!'); })
So let’s put this all together in our saveEmployee method :
saveEmployee : () => { if ((app.employee.firstName) && (app.employee.lastName)) { let saveEmp = { firstName : app.employee.firstName, lastName : app.employee.lastName, jobTitle : app.employee.jobTitle, dept : app.employee.dept, age : app.employee.age } if (! app.employee.id) //check if the id has not been set app.db.collection(‘Employees’).add(saveEmp) .then(() => { app.clearEmployee() }) .catch(() => { console.log(‘Error saving employee ‘ + app.employee.firstName + ‘ ‘ + app.employee.lastName) }) else { //if the id has been set, we save an update let docRef = app.db.collection('Employees').doc(app.employee.id) if (docRef) docRef.set(saveEmp) .then(() => { app.clearEmployee() }) .catch(() => { console.log(’Error updating employee ‘ + app.firstName + ' ' + app.lastName) }) } } else alert(‘You must enter both a first and last name to save.’) },
This is fairly verbose and we could certainly tidy things up but it works and it’s verbosity helps to explain what it is we’re doing so we’ll leave this as it is. Before we close this part of the tutorial off and let you go and get a much deserved coffee, let’s add one more little piece of functionality to our app. Things are great, but let’s say the user clicks to Edit an employee and realises they don’t want to save - at the moment there’s no way for them to go back to add a new one. To get around this we’ll put an Add button next to the Save button that let’s them click to add a new employee. This button however should only be available if they’re editing an existing employee.
<label>Age</label> <input type=“number” v-model:number=“employee.age”></br> <button v-on:click=“saveEmployee()”>Save</button> <button v-on:click=“clearEmployee()”>Add</button> </div>
Notice we’re directly calling our clearEmployee method as our event handler as that does everything we need to put our app into Add mode. Cool, but remember we only want to show this button if the user is in Edit mode. The way to do this is to use Vue’s conditional directive, v-if. This lets us include a conditional statement on the element, whether directly of via a method call, to determine if it should be visible on the page. In this case we want to check if the current employee object has an id set, if it has then we’re in edit mode so show the Add button, otherwise don’t show it.
<button v-if=“employee.id” v-on:click=“clearEmployee()”>Add</button>
That’ll do for this part of the tutorial dedicated to the U in CRUD. In this part we’ve covered retrieving each document’s unique id along with the other fields. We’ve added an Edit button for each employee in the list and exposed an index from our v-for directive to assign an employee index from our array so each button knows which employee it relates to. We‘ve saved our employee updates back to the database using the unique id of each document and finally we covered using v-if to conditionally show a button on our page. In the next part of this tutorial we’ll cover the D in CRUD - deleting. Hope you can join me.
You can download the completed code for this part of the tutorial on Github using the repo below and select the part4 folder. https://github.com/MancDev/VueFire
1 note
·
View note
Text
VueJS & Firebase Cloud Firestore Stripped-Back - Tutorial Part 3
In this tutorial series we are using a stripped-back approach to getting started with Vuejs & Firebase Cloud Firestore to build full-stack web applications. No CLIs or build tools, just a HTML page.
Retrieving realtime data
In part three of this tutorial we’re covering the R in CRUD, reading or retrieving data. We’ll cover querying the employee data we want, retrieve and display it in our app page and make it all work in realtime. If you haven’t read the previous part of the tutorial you can get it here, if you haven’t read the first part you can do so here. In the previous part we covered creating employee records and in this part we want to display those records in our app so you can see instantly when a new record has been added. We want to get the data back at the very beginning of our app so the list of existing employees displays immediately. To achieve this we’ll need to add the code to retrieve the data into the created lifecycle hook. We then declare a query based on the collection we want to retrieve from eg Employees
created : function() { let that = this that.db = firebase.firestore()
let query = that.db.collection(’Employees’)
}
Note : We covered the issue of using this in part 2. We resolved it in our functions inside of the methods object by using arrow functions rather than standard functions and referencing the Instance Object explicitly eg app. However as Vue recommends not to use arrow functions for lifecycle hooks like created, I’ve opted for consistency by assigning this to a variable called that and refering to that throughout the lifecycle hook function when referencing the properties and methods of the Vue Instance. Now we have a Firestore query object assigned which points to the collection we want, we can call a method on that object to actually retrieve the data from Firestore. There are a couple of methods we can use to grab the data back from the query but we want to get it in realtime. When we say realtime we essentially want to retrieve the data from the query immediately and then listen for any changes in the data which occur so we can act on those changes as they happen. So once we retrieve data in realtime we can react to changes in the same way as you would upon initially retrieving it - to display them on our page using Vue’s directives and data binding. The next thing we need to do is to a declare a new array in the data section of our Instance Object that we can retrieve the employee data into and display on the page.
var app = new Vue({ el : ’#app’, data : { appTitle : ‘EmployeeMagic’, mainStyle : { ‘margin’ : ’20px’ }, employee : {firstName : ‘’, lastName : ‘’, jobTitle : ‘’, dept : ‘’, age : 0 }, db : {}, employees : [ ] },
Now we have our employees array, we can go back to our created function and run our query using Firestore’s onSnapshot() method. onSnapshot() expects a function that will be triggered initially and then whenever the data changes. This is what is termed a callback function. onSnapshot() is unusual amongst Firestore methods as it uses a callback rather than returning a promise to handle asynchronous calls to the database and it’s important to note the difference. If we had used an alternative method to onSnapshot() to retrieve the data, such as get(), it would return a promise instead (as we covered in part 2) however in that circumstance the code you implemented to manage the data retrieval would only ever be called once which is easier to manage with a promise rather than, in this case, potentially being run multiple times whenever any data changes occur. The function we pass to onSnapshot() receives a snapshot parameter which is an object containing the actual data that comes back from Firestore in response to our query.
created : function() { let that = this that.db = firebase.firestore() let query = that.db.collection(’Employees’) query.onSnapshot((snapshot) => {
}) }
In our callback function, we pick up the snapshot and manipulate it to retrieve the data into our employees array. Remembering that this function will be run each time data is changed on our queried data, not just the first time it’s run, we need to empty our employees array so we don’t end up displaying duplicate data.
Our next step is to use a method that’s included as part of the snapshot object, forEach(). The forEach() method receives another callback function that it runs for each employee as it iterates over every retrieved document in the snapshot. In the callback function that we pass in, we tell it what is to happen for each employee, in our case we want it to add each document to our employees array. Our forEach callback function receives a parameter which is the document or employee for the current iteration, in our example we call it doc. We can then use doc to extract the fields from the current document and into our array using the array’s push() method.
query.onSnapshot((snapshot) => { that.employees = [ ] snapshot.forEach((doc) => { that.employees.push({ firstName : doc.data().firstName, lastName : doc.data().lastName, jobTitle : doc.data().jobTitle, dept : doc.data().dept, age : doc.data().age }) })
})
To get the actual field information out of each document we have to call it’s data() method first followed by the field name we want. So far so good. We’ve got our data out of our Firestore and into an array we can use on the page and we’ve got a change listener so whenever any data is added, changed or deleted we know the list will be refreshed in realtime. Our next task is to use Vue to display the data on the page.
We’re going to add a table to our page which we’ll style a little bit with Bootstrap and we’ll add a new style to Vue, as we covered in part 1, to put a margin at the top of the table.
var app = new Vue({ el : ’#app’, data : { appTitle : ‘EmployeeMagic’, mainStyle : { ‘margin’ : ’20px’ }, tableStyle : {’margin-top’ : ‘20px’}, employee : {firstName : ‘’, lastName : ‘’, jobTitle : ‘’, dept : ‘’, age : 0 }, db : {}, employees : [ ] },
In the template we’ll apply our style and insert a table with the column headers. Note we’re including one column to display the full name rather than separate columns for the first and last names.
<div :style="tableStyle"> <table class="table table-bordered table-striped"> <thead> <th>Name</th> <th>Job Title</th> <th>Department</th> <th>Age</th> </thead> </table> </div>
Our next step is to display the actual data. Like most frameworks, Vue has a mechanism to iterate through arrays and repeat part of a template based on the elements in the array. In Vue this is the v-for directive and we’ll use it to repeat a <tr> tag for each employee in our employees array that is populated with data we retrieved from Firestore.
</thead> <tbody> <tr v-for=“employee in employees”>
Next we’ll add in the columns of information and to display the information from our array, we reference employee (as defined in our v-for directive) and the property name using data binding with curly braces.
<td>{{ employee.firstName }} {{ employee.lastName }}</td> <td>{{ employee.jobTitle }}</td> <td>{{ employee.dept }}</td> <td>{{ employee.age }}</td> </tr> </tbody> </table> </div>
If you now save and refresh you should now see the employee records you entered so far displayed when your app runs. Just to ensure your realtime updates are working, add a new employee record and click Save and your list will automatically refresh to include the new record. Pretty cool.
We’ve almost done with the R part of CRUD but we’ll just cover one more thing before you go and make yourself a cuppa tea. You might notice that the data isn’t sorted in any particular order and it’s a bit untidy. Ideally we would like to sort the data by name, specifically the employee’s last name. This is really easy to do with Firestore. In the created hook function, go to the line where you declare the query object. At the moment we’re just referencing the Employees collection, we’re not filtering the list with a query and we’re not sorting on it, we’re just getting everything in any order Firestore feels like sending it to us. To sort this query by the employee’s last name all we need to do is add a call to the orderBy() method and pass in the field name you want to sort by.
created : function() { let that = this that.db = firebase.firestore() let query = that.db.collection(’Employees’).orderBy(’lastName’) query.onSnapshot((snapshot) => {
Save your file and refresh your browser and you’ll notice your list of employees is now sorted by the employee’s last names. Easy eh? In a future part of this tutorial we’ll cover more about sorting data and also querying to retrieve filtered data.
In this part of the tutorial we’ve covered how to setup a query to retrieve a collection from Firestore and how to retrieve updates to the collection in realtime. We’ve covered callbacks to trigger code whenever changes are made and how to populate an array in our Vue object from data in a Firestore collection and then use a table to display that data using Vue’s v-for directive and data binding and finally how to sort the data coming from Firestore. In the fourth part of this tutorial we’ll continue with CRUD, addressing the U for updating where we’ll cover how to edit and update an employee. Look forward to your company :)
You can download the completed code for this part of the tutorial on Github using the repo below and select the part3 folder. https://github.com/MancDev/VueFire
0 notes
Text
VueJS & Firebase Cloud Firestore Stripped-Back - Tutorial Part 2
In this tutorial series we are using a stripped-back approach to getting started with Vuejs & Firebase Cloud Firestore to build full-stack web applications. No CLIs or build tools just a HTML page.
Putting the ‘C’ in CRUD
Welcome back to our Vue and Firebase stripped back tutorial. If you skipped part 1 of this tutorial you can get it here.
Let’s start straightaway from where we left off in part 1 and start looking at how we can develop our employee tracker app, EmployeeMagic, to let the user add records to their employee database. This represents the C in CRUD (Create, Read, Update, Delete).
We’ll start by adding some input boxes to our template for the user to be able to add employees. The fields we want are first and last names, job title, department and age.
Let’s start by adding a First Name field.
<body>
<div id=“app”>
<div v-bind:style=“mainStyle”>
<h1>{{ appTitle }}</h1>
<label>First Name</label> <input type=“text” v-model=“firstName”>
</div>
</div>
</body>
Notice the input element, we’ve added a Vue directive to it called v-model and we’ve assigned it a value of firstName. For AngularJS devs this will seem very familiar. v-model is a data binding directive that binds the input element to a variable in our view model (the data in the Vue Instance Object). Therefore whatever value the user keys into the input box will be automatically assigned to a variable in your Vue Instance Object. Now we don’t presently have a firstName variable in data in our Instance Object so we need to add it and assign it an initial value, in this case an empty string.
<script> var app = new Vue({ el : '#app’, data : { appTitle : ‘EmployeeMagic’, mainStyle : { ‘margin’ : ’20px’ }, firstName : ‘’ } }) </script>
Just to prove that our data-binding in Vue is working and is in fact working in both directions (from the template to the Instance Object and from the Instance Object to the template), also called two-way data binding, we’ll stick a double-curly on the end of the input box eg
<input type=“text” v-model=“firstName”>{{ firstName }}
Save the file and refresh the page in your browser. As you begin to type in the First Name box, the name is displayed at the side of it. The input is assigning it to the firstName variable (via the v-model directive) and Vue is watching for changes in the firstName variable and when it detects one it changes anywhere in the page where firstName has a binding, in this case via the double-curly.
OK, enough of the fun and games, let’s add our other fields to the template and create their corresponding variables in the data section. As we’re grouping several fields together to form an employee we’ll change it to put them all in their own object called employee and change the template to bind the input boxes to the relevant properties on the employee object instead. We’ve proved our point with the two-way data binding for now so we’ll remove our double curly.
<body>
<div id=“app”>
<div v-bind:style=“mainStyle”>
<h1>{{ appTitle }}</h1>
<label>First Name</label> <input type=“text” v-model=“employee.firstName”></br>
<label>Last Name</label> <input type=“text” v-model=“employee.lastName”></br>
<label>Job Title</label> <input type=“text” v-model=“employee.jobTitle”></br>
<label>Dept.</label> <input type=“text” v-model=“employee.dept”></br>
<label>Age</label> <input type=“number” v-model:number=“employee.age”></br>
</div>
</div>
</body>
Notice that on the Age input we’ve added an extra command to the v-model directive, :number. This is very useful in Vue as, although the type of the input is set to number so the user cannot input anything other than valid numbers, by default when the value is converted by the binding to Vue data properties, it is converted as a string. As we want the age property to remain a number type, the v-model:number directive is required. <script> var app = new Vue({ el : '#app’, data : { appTitle : ‘EmployeeMagic’, mainStyle : { ‘margin’ : ’20px’ }, employee : {firstName : ‘’, lastName : ‘’, jobTitle : ‘’, dept : ‘’, age : 0 } } }) </script> We’ll next add a Save button which will write the entered employee to the database. To carry out the task of saving when the user clicks the Save button we need to hook the button into our Vue Instance Object and call a function that’s run to save our new employee record. To do this we need to use another Vue directive called v-on:click
<label>Age</label> <input type=“number” v-model:number=“employee.age”></br>
<button v-on:click=“saveEmployee()”>Save</button>
</div>
</div>
</body>
v-on is used to handle events and the :click is the specific event to be triggered on. We then assign the function that we want to run once the event (click) is triggered, in this case saveEmployee().
If you remember, in part 1 of this tutorial we mentioned that when using the v-bind directive along with a colon-command that you could use shorthand instead by dropping the v-bind eg v-bind:style could be entered as just :style for short. The same applies to the v-on directive however as it’s an event, rather than using the colon, you must use a @ instead. Therefore instead of using v-on:click you could use the shorthand version which is @click. Ultimately it’s a matter of preference and usually I would use the shorthand but to provide full explanations in these tutorials we’ll stick to using the verbose style.
We now need to write our saveEmployee() click handler function. This involves declaring functions or methods in our Vue Instance Object which you can do by declaring a new first-level object on the Instance Object called methods. All of your logic for our app will live in functions declared in Vue’s methods object. <script> var app = new Vue({ el : '#app’, data : { appTitle : ‘EmployeeMagic’, mainStyle : { ‘margin’ : ’20px’ }, employee : {firstName : ‘’, lastName : ‘’, jobTitle : ‘’, dept : ‘’, age : 0 } }, methods : { saveEmployee : function() { alert(‘You have saved ‘ + this.employee.firstName + ‘ ‘ + this.employee.lastName) �� } } }) </script> So, you can see that we’ve declared a saveEmployee function within the top-level methods object. In the above we’re not saving anything, we’re just going to display a standard alert message which includes displaying the last name and first name of the employee we’re saving. Notice that we’re using this to refer to our employee properties. Any variable declared in the data object of our Instance Object that we want to reference in our methods must be referenced using this, otherwise Vue won’t recognise it. As anyone who has worked with Javascript for any length of time knows, using this can be problematic as what it points to can change depending on the context from where it was called. There are ways around using this which we’ll discuss later but we’ll just stick with it as it is for now. Save the file, refresh your browser and enter the details of an employee, click Save and you should see the alert message appear. We’re now at the point of needing to be able to physically save our new employee to our Firebase Cloud Firestore. To get started with this you just need a bog-standard Google account. If you’ve got a gmail account or use any of Google’s products where you need to log-in then you’ve got a Google account. If not then you’ll need to create one which you can do on the Firebase site. To get started go to the Firebase web-site (https://firebase.com) and click Go To Console. If you’re not signed in to your Google account or you don’t have one you’ll then be taken to the usual sign-in/sign-up pages to get going, otherwise you’ll be taken to the Firebase console. Click Add Project and enter a project name and select your region. Once in the main console click the Database option on the left. You’ll be given the option of Realtime Database or Cloud Firestore. Make sure you select Cloud Firestore. You’ll also be asked how you want the security level to be setup, make sure you select Development/Test mode otherwise you’ll have to start setting up security rules manually which are a whole other subject altogether. Your Cloud Firestore will then be setup, which might take a short while. Once your database is setup we don’t need to do anything else in the database console however we do need to grab the code and libraries for getting Firebase into our app. To do this click the Project Overview option on the left. In the Get Started page click on the option for Add Firebase to your web app. In the window that pops up you’ll see a code snippet, just click the Copy button. Go back to your editor and paste the code snippet below the closing </body> tag and above the VueJS CDN script tag as shown below : </body>
<script src="https://www.gstatic.com/firebasejs/4.9.1/firebase.js"></script> <script> // Initialize Firebase var config = { apiKey: "AIzaSyA0KlHuISpQL1F0XMWv1FfmtbaJQmPKwqQ", authDomain: "vuefire-da3bf.firebaseapp.com", databaseURL: "https://vuefire-da3bf.firebaseio.com", projectId: "vuefire-da3bf", storageBucket: "vuefire-da3bf.appspot.com", messagingSenderId: "1094070580435" }; firebase.initializeApp(config); </script> <script src="https://cdn.jsdelivr.net/npm/vue"></script><script> var app = new Vue({ The version of the Firebase CDN link above (4.9.1) is current at the time of writing (Feb 2018) but is likely to be different by the time you are reading this, always use the link copied from the Firebase page. This details in the config object are specific to my account and project and yours will certainly be different, make sure you keep the code exactly the same as the code snippet copied from your Firebase project. The code snippet from Firebase however doesn’t include a CDN for Cloud Firestore, this may well be due to it still being officially in beta at the time of writing and may well be included in the standard code snippet by the time you’re reading this so it’s worth checking. At this point in time however, we’ll need to add it manually and the best way to do it is just to copy the line for the firebase CDN link for firebase.js, paste it below it and change the new line from firebase.js to firebase-firestore.js as below : <script src="https://www.gstatic.com/firebasejs/4.9.1/firebase.js"></script> <script src="https://www.gstatic.com/firebasejs/4.9.1/firebase-firestore.js"></script> Save the page and refresh in Chrome with the Console open and make sure there are no errors displayed before proceeding and if there are check you have followed everything above correctly. We’ve now got our libraries for Cloud Firestore and the code snippet will initialise Firebase to enable you to utilise your newly created database. Now we have to let our Vue app know about it. What we need to do is to create a Firestore object within our Vue app when it first runs so it’s available to us throughout our app. To ensure that our Firestore object is created at the beginning during initialisation of our app, we need to utilise a lifecycle hook that Vue provides, called created. Lifecycle hooks are effectively events that occur during Vue’s internal processing of our Instance Object. Vue exposes these events to us so we can hook into them and carry out some tasks at that specific point in our Vue Instance Object’s processing lifecycle. With the created hook, we can attach the tasks we want to carry out when the Vue Instance Object is first created, which is exactly what we need to do to generate a Firestore object to ensure we can access the database throughout the app. We’ll first initialise an object in our data section called db that we’ll use to access Firestore. We’ll then include a created hook which is a function containing our initialisation code. In this case we just want to assign an object from Firestore that we can use :
<script> var app = new Vue({ el : ’#app’, data : { appTitle : ‘EmployeeMagic’, mainStyle : { ‘margin’ : ’20px’ }, employee : {firstName : ‘’, lastName : ‘’, jobTitle : ‘’, dept : ‘’, age : 0 }, db : {} }, methods : { saveEmployee : function() { alert(‘You have saved ‘ + this.employee.firstName + ‘ ‘ + this.employee.lastName) } }, created : function() { this.db = firebase.firestore() } }) </script> We’re now plugged into our Firestore through our db object. It’s now fairly straightforward to save our data to Firestore. We use Firestore’s add() function to save our employee object which, as we covered earlier, is automatically populated through data binding when the user enters the information into the input boxes. To call add() we must tell Firestore which Collection to save the new object to. Collections in Firestore represent a formal declaration of the documents or objects we’re going to save. This makes it much more like the Tables and Records we’re used to in relational databases, where tables are collections and records are documents. In this case we’ll save it to a collection called Employees - note that we didn’t explicitly setup the Employees collection previously, it will be automatically created for us by Firestore if it doesn’t already exist. We’ll remove the alert from our saveEmployee function and replace it with the line to save the new employee. methods : { saveEmployee : function() { this.db.collection(‘Employees’).add(this.employee); } }, Again notice that we’re referencing db using this, as we are with employee (and all of our data properties). We’ll review this shortly but it’s important to note it for now. Save the file in your code editor and refresh it in Chrome. Enter the details of an employee and click Save. Nothing will appear to happen on the page, however open your Firebase console, select Database and you’ll see your new record under the Employees collection. Notice that the record has a strange looking unique Id automatically assigned to it, we’ll be utilising this in another part of the tutorial. Let’s tidy up the saving process. Firstly we want to make sure it doesn’t save unless something is entered into both the First and Last Name fields. Secondly we want some visual indication that you have saved to the database, specifically the input boxes are cleared ready for another new employee. In order to clear the employee inputs we’ll implement a separate function called clearEmployee() so we’re not cluttering up our save function. methods : { saveEmployee : function() { if ((this.employee.firstName) && (this.employee.lastName)) { this.db.collection(‘Employees’).add(this.employee) clearEmployee() } else alert(‘You must enter both a first and last name to save.’) }, clearEmployee : function() { this.employee = { firstName : ‘’, lastName : ‘’, jobTitle : ‘’, dept : ‘’, age : 0 } } }, Note 2-way data binding, changing the properties in the view model reflects in the input boxes that they’re bound to. It’s important to note that Firestore’s add() function is asynchronous so any code that comes after it, such as clearing the input fields, will be executed before it can be determined if the employee was saved correctly. Therefore rather than simply call the clearEmployee() function immediately after add() as we have done above, we’ll need to utilise the promise which Firestore’s add() function returns and put any code we want to execute after it’s successfully saved into the returned promise’s then(). saveEmployee : function() { if ((this.employee.firstName) && (this.employee.lastName)) this.db.collection(‘Employees’).add(this.employee) .then(function() { this.clearEmployee() }) .catch(function() { console.log(‘Error saving employee ‘ + this.employee.firstName + ‘ ‘ + this.employee.lastName) }) else alert(‘You must enter both a first and last name to save.’) }, Here we’ve chained a then() to the add() function call and passed in a function which calls the clearEmployee() to clear the properties for the input boxes only after we’re sure of a successful save. We’ve also chained a catch() so any saving errors are logged to the console.
A brief explain of Promises
If you’re not familiar with promises, they’re simply a way to handle asynchronous code. Asynchronous functions are just standard functions which carry out some processing but return control back to the caller before completing it’s process (to avoid blocking the app’s execution). Async functions usually return a promise object which enables the code which called the function to implement any tasks that needs to execute only after the async function has fully completed it’s process. In this instance we only want to clear the input boxes once we know that the employee has definitely saved. To do this we call then() which is a function on the returned promise object. then() accepts a function as a parameter which we use to execute any code we want after we know the save has completed, in this case to clear the employee properties and inputs. As we’ve done above you can also chain a call to the promise’s catch() function to execute any code should the async function not succeed. With all of that said, the new changes we’ve just made WILL NOT WORK!! And they will not work because of the this issue that was foretold. The reference to this changes depending on the context in which it was called. Therefore when we come to execute the code that is passed to the then() or catch() functions, this points to something else other than our Instance Object data and we’ll get errors! There are a couple of ways around this. The traditional way is to simply assign this at the beginning of a function to another variable, usually called that, and then reference that instead of this throughout the function to be sure that you’ll always reference the desired object. This works but it means you’re always having to remember to implement it at the beginning of every method. The other way is to reference the Instance Object explicitly where you know this won’t work for you. In this tutorial we declared a variable to contain our Vue Instance Object called app eg var app = new Vue({. You can therefore call app.clearEmployee() instead of this.clearEmployee() in the then() function and it would work. The problem is that context again gets in the way and you cannot use an explicit reference to the Instance Object in it’s own context so saying app.db.collection(‘Employees’).add(app.employee) in the main saveEmployee() function won’t work either, so you’d end up using this in some places and app in others.
The ideal solution would enable us to use one way throughout to refer to the properties and methods on your Instance Object to ensure consistency but without additional workload that you would need to remember to implement on each method. Fortunately ES6/ES2015 introduced arrow functions which help us resolve this problem. With arrow functions we can use an explicit reference to the Instance Object in every context throughout our methods. Arrow functions, from a declarative standpoint, simply change a function declaration from : function() { }
to () => { } So let’s change our saveEmployee() function from a standard function declaration to an arrow function and do the same with the then() and catch() functions. Then replace every reference of this to app. saveEmployee : () => { if ((app.employee.firstName) && (app.employee.lastName)) app.db.collection(‘Employees’).add(app.employee) .then(() => { app.clearEmployee() }) .catch(() => { console.log(‘Error saving employee ‘ + app.employee.firstName + ‘ ‘ + app.employee.lastName) }) else alert(‘You must enter both a first and last name to save.’) }, For consistency purposes we should also change our clearEmployee() method as well clearEmployee : () => { app.employee = { firstName : ‘’, lastName : ‘’, jobTitle : ‘’, dept : ‘’, age : 0 } } The only exception to this rule is the created lifestyle hook function where the VueJS docs expressly recommends not declaring it as an arrow function so we’ll leave it as a standard function declaration (including referencing this rather than app). Like with Promises, arrow functions are a fairly new addition to JS so you must be using an evergreen browser, like Chrome, which implements them natively otherwise you’ll need to use a transpiler such as Babel. We’ve now completed the Create task from our CRUD list. In this tutorial we’ve added input boxes and bound them to properties on our view model to create 2-way data binding. We’ve added events and methods to our Instance Object, created a Firebase Cloud Firestore project and hooked it into our app, covered Vue’s lifecycle hooks to initialise a reference to our database and then covered how to save employees to our Cloud Firestore. Finally we covered the issues with using this and how arrow functions can help us overcome these problems. In the next part of the tutorial we’ll cover the R in CRUD, Reading (or Retrieving) employees from our database. We’ll cover getting data back from our Firestore and explain how to implement realtime updates. I hope you’ll join me.
You can download the completed code for this part of the tutorial on Github using the repo below and select the part2 folder. https://github.com/MancDev/VueFire
1 note
·
View note
Text
VueJS & Firebase Cloud Firestore Stripped-Back - Tutorial Part 1
In this tutorial series we will explain how to start developing with the Vuejs framework & Firebase Cloud Firestore to build full-stack web applications. We’ll start from scratch by stripping everything back to the basics - no CLIs, build tools or bundlers, just a HTML page, an editor and a browser so you can get to grips with building apps with Vue & Firebase more quickly and be productive faster.
VueJS
Vuejs is now one of the big 3 Javascript frameworks in the web client ecosystem. However it’s not developed or backed by a huge corporation, it was developed by one individual, Evan You. The proliferation of JS frameworks in recent years has led to the term “Javascript Fatigue” where developers have become weary of all of the new and self-proclaimed “latest and greatest” open source frameworks. It is in this environment that Vue has emerged through all of the noise to become a major player alongside Angular and React (both backed by huge technology companies, Google and Facebook respectively). That Vue has achieved such a status in this environment backed by one individual highlights just how Vue has hit a chord with developers and is filling a gap that isn’t being met by Angular, React or other competitors like Ember and Aurelia.
Evan You is an ex-Googler who was familiar with AngularJS (often referred to as Angular 1) and used it for internal projects at Google that he was working on. He set out to achieve a framework that delivered the benefits of AngularJS but was more lightweight, faster and better suited to smaller apps that didn’t require the heavy-lifting of the huge apps that AngularJS was originally built to develop such as their Adwords platform and their internal CRM system. What he produced was a beautifully simple solution and it quickly attracted adoption as the front-end framework of choice for many PHP developers, particularly from the Laravel community.
Despite promising early adoption in some quarters, Vue may well have stayed a “me-too” framework in the ecosystem had it not been for Google’s decision to redevelop their AngularJS framework from scratch. Angular (often referred to as Angular 2) was such a departure from the original AngularJS framework that it introduced a fundamental shift in approach and required developers to learn many new concepts just to build basic apps. Developers were kept in limbo between AngularJS and Angular for almost 2 years, with many alphas and then betas and many breaking changes and missing pieces to deal with during the transition and then no easy migration path for their existing code and apps.
There’s no doubt that, as a new framework in it’s own right, Angular is a fantastic, powerful, all-in-one next generation framework but it isn’t “Angular” as millions of developers know it. Perhaps the biggest mistake Google made was in not launching their next-gen framework under new branding. In any event and unfortunately for Google, Angular 2 was too big a change for many developers and while uptake was initially high, it hasn’t achieved the type of ongoing adoption or love as AngularJS or React and it’s clear that Vue, with many obvious similarities to the original Angular, has arrived just in time to sweep up and become a magnet for disgruntled Angular devs.
In addition, the droves of developers jumping in to the Javascript ecosystem over the past years, in their evaluation of an appropriate framework, are choosing Vue in vast numbers due to how easy it is to learn and how quickly they can start building apps. I would say that the best word to describe Vue to developers choosing a framework is “approachable” - it seductively invites you in and once there you find it’s so intuitive and simple to get productive, covering all of the bases you need, that once you’re in you tend to stick with it. For me personally I actually enjoy spending my days developing around Vue, I cannot say the same for Angular unfortunately.
In 2018, Vue is set to gain even greater momentum and overtake Angular into second place in the Javascript framework popularity chart. The main reason for this may be that the massively popular Ionic mobile and PWA framework is about to release a version that decouples it from Angular and enables developers to build apps with Ionic using any framework (or none at all). It’s interesting that this is likely to be a tipping point for Vue to achieve critical mass and yet is due to the Ionic team’s concern that Angular isn’t seeing the level of adoption anticipated and continuing to hitch their wagon to Angular is likely to hamper their own growth.
To address this, in Ionic 4, they’ve developed a framework-agnostic, web component-based edition of their mobile framework. When you look online it’s fairly clear that the majority of the delight at Ionic’s shift away from Angular is coming from those who want to use Ionic with Vue. Personally I only stuck with Angular because of Ionic despite my preference for Vue and since their announcement of Ionic 4, I have thrown myself fully into Vue. The sweet spot that Vue hits between Angular and React is in delivering a more lightweight and simple approach than Angular, focussing first and foremost on being a view renderer like React but providing optional elements that are officially supported and developed by the Vue core team, such as routing that can be easily dropped in to your app. This is what is meant when Vue is called a “progressive framework”, you can start by using as little or as much of the framework as you need, progressively using more of it’s subsidiary elements as required. Another advantage to using Vue is that it’s lightweight enough to use it in one part of your app and progressively expand it to other parts when you’re ready, for example if you have existing app in vanilla Javascript, jQuery or another framework that you want to change over to Vue piece by piece.
As mentioned, Vue’s biggest advantage is it’s simplicity and approachability. While other frameworks require knowledge of build systems, CLIs, Node, NPM etc just to start building an “Hello World” app, with Vue you can strip everything right back, open up an HTML file in an editor and get started simply without needing to spend time learning anything else. While you’ll likely want to move over to the full modern JS development environment as you get more involved, it isn’t required to get started.
Firebase Cloud Firestore
So Vue is a fantastic front-end framework but to build even the most trivial worthwhile app, we need a back-end and a data store. Like with using Vue, we want to use something that delivers simplicity and approachability yet gives us the power to build advanced apps as you become more experienced. For this project Firebase Cloud Firestore is a no-brainer to use as the database back-end. Like Vue, we can just get started using Firebase with just a basic HTML page - no need for CLIs and build tools to just start building something.
I first used Firebase back in 2014, when they were a small, private company shortly before they were acquired by Google. At the time Firebase was not a platform but a hosted realtime database and I fell in love with it immediately - for prototyping it was perfect and it’s realtime capabilities were just awe-inspiring at the time.
However Firebase did have some serious limitations that made it unsuitable for me to use as the back-end database in a production app. It didn’t allow server-side code, so all of your logic was exposed on the client and it’s database querying facilities were extremely limited. Also, as a No-SQL document database, organising relational-style data into something that was manageable without joins and queries required denormalisation and duplication of data, something which is anathema to those coming from a SQL-based relational database background. I felt it was a real shame as, despite these limitations, it was very impressive and working with it’s Javascript API was a joy.
After Google’s acquisition, Firebase was expanded into a full serverless platform allowing for storage, cloud messaging, hosting, authentication, analytics and much much more. What had been “Firebase” became the Realtime Database element of the platform. Fortunately Google started to address the limitations that I and many other developers had found with the original Firebase. First server-side code, in the form of Cloud Functions, was added which enables you to put more of your sensitive business logic code onto the server.
More recently Google introduced an alternative database to the original realtime database which they call Cloud Firestore. Cloud Firestore addresses many, but not all, of the issues with the realtime database in terms of querying and organisation of data. It still provides the full realtime capabilities that we know and love and is still a No-SQL database based on documents. However you can now organise them into Collections (similar to relational DB Tables) which enables you to perform much more advanced queries. You can have specifically defined fields each of which can have a specific type. One of these types is the Reference type which lets you store links between documents on different collections to enable a form of join. In addition Cloud Firebase enables offline usage so the user can continue to use your app even if access to the server isn’t available. There are still limitations, remembering it’s not a relational database, and full joins are not possible and neither are aggregate queries such as SUM, COUNT etc. However with 90% of the issues I had with the original Firebase realtime database now dealt with through Cloud Functions and Cloud Firestore, Firebase is now an excellent choice as the back-end, serverless platform and data store for building full-scale production apps.
OK so enough talk about the what and why, let’s get going with the how and write some code. We’re going to start, as we’ve talked about, with a single HTML page. Choose your OS, code editor and browser of choice (I’m using VSCode on OSX and highly recommend using Chrome as your browser).
Open up your editor and select to create a new file in a new folder. Just call the new file index.html. Once you’ve done this, start with a skeleton html page as shown below :
<html>
<head> <title>VueJS & Firebase From Scratch</title> </head>
<body>
</body>
<script>
</script>
<html>
The first thing we’ll need to do is to import the VueJS library. We can do this with a CDN link (see below) which is placed below the closing body tag and before the opening script tag :
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
This recommended CDN link is correct at the time of writing however may change in future. To ensure it’s correct, once you’ve inserted this link into your page, save it and open it up in Google’s Chrome browser (select File from Chrome’s menu and select Open File … to navigate and select your newly created index.html file). The page will show as blank in any case however right-click on the page and select Inspect from the pop-up menu and click the Console tab. If this displays nothing eg there are no error messages then you’re good however if you do get an error complaining about the vue library then browse to https://vuejs.org/v2/guide/ and look for the link displayed under Getting Started.
We’re ready to build our app! The first thing we need to do is to create the Vue Instance Object that will be used as the core of your app.
<script> var app = new Vue({ el : '#app' }) </script>
This creates a new Vue instance that we can reference throughout our html template and which contains all of the data and methods that we’ll create to develop the app. Some call it the View Model however we’ll stick to calling it the Instance Object. We simply create a new object called app from Vue and pass in an object with all of the information about our app. To begin with, we only declare a single property in this object which is el, short for element, and we assign #app. This tells Vue that any reference to an element that has the id of app is to be controlled by our Vue Instance Object.
In our html template we can now simply create a container html tag and assign it the id of app. All of the content within that tag is then controlled automatically by our Vue instance object. (Please note that any content in bold from here on in denotes new or changed content.)
<body>
<div id=‘app’>
</div>
</body>
Please note : Unlike AngularJS, with Vue you cannot assign the reference to the Instance Object on the body tag, you have to create your own container tag to assign it to, as in this case we’re assigning it to a div container.
Great but we’re not actually doing anything yet. Now we want to give our app some life. To do this we’ll give the app a name and display it as a title and we’ll do this as data held on the Instance object rather than write it directly on the template. To do this we’ll need to add another property called data to our instance object. The data property is an object which contains any variables you want to reference in your template or elsewhere in your app. In this case we’ll create a variable called appTitle and assign it a name. The app we’re going to build is an employee tracker that is going to be so indispensable to it’s users that it will be like magic!
<script> var app = new Vue({ el : '#app’, data : { appTitle : ‘EmployeeMagic’ } }) </script>
We can now use the data binding features of Vue to display our new app title in our page template.
<body>
<div id=“app”>
<h1>{{ appTitle }}</h1>
</div>
</body>
Save and refresh your page in Chrome and you’ll see EmployeeMagic as your app header. The double curly braces are pretty standard in most frameworks these days to denote data-binding (also called interpolation). The content inside the double-curlies is interpreted by Vue and the required content is displayed at that point in the template. In this case Vue recognises that we have a variable called appTitle in data in our Instance Object and replaces it in our template with the value contained in the variable. There are many benefits of data-binding, the main one being that any change in the variable in our instance object is automatically reflected in our template without any additional effort on our part.
So far so good but it’s a little plain-Jane so let’s add the Bootstrap library link for a bit more aesthetic to it without any extra effort. The link used below for then Bootstrap CDN was current at the time of writing however check the Bootstrap website if you have trouble with the link :
<header> <title>EmployeeMagic</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.3/css/bootstrap.min.css"/>
</header>
Let’s say however we want to add a margin around the app so it’s not displaying right up against the edge all the time. To do this we’ll need to add some CSS styling however we can take advantage of a cool Vue feature which lets us set our styles programatically.
Let’s add another variable to our data object which is specifically for styles, let’s call it mainStyle. This will be an object so that we can assign as many different CSS style settings as we like. For now we just want to assign a margin of 20 pixels :
<script> var app = new Vue({ el : ‘#app’, data : { appTitle : ‘EmployeeMagic’, mainStyle : { ‘margin’ : ‘20px’ } } }) </script>
Now we need to tell Vue where to use that styling property in our template. We’ll create a new container div tag to wrap around the rest of our app. In that tag we need to tell Vue to assign the styling we’ve defined in mainStyle. To do this we can use a Vue directive called v-bind:style and assign it the name of style object we want to use.
<body>
<div id=“app”>
<div v-bind:style=“mainStyle”>
<h1>{{ appTitle }}</h1>
</div>
</div>
</body>
Save and refresh in Chrome and you’ll see the margin has been applied. I personally love this feature of Vue and there are lots more you can do to apply styling which we’ll cover later in the tutorial.
It’s interesting to note that when using directives such as v-bind:style, Vue offers a more shorthand way by dropping the v-bind. If Vue just sees :style it knows what to do, so we could have used ...
<div :style=“mainStyle”>
... instead. Throughout these tutorials I’ll continue to use the more verbose version to show the full directives consistently however where a new directive is shown, I’ll also highlight the shorthand version. Generally if you see v-bind followed by a colon and the command, you can drop the v-bind although there are exceptions that we’ll cover in a future part of the tutorial.
We’ve so far covered what VueJS and Firebase Cloud Firestore are, why we’re using them as the framework and platform for this app, and the basics of setting up a simple HTML page to build our employee tracker app, setting up our Vue object, basic data-binding and styling using a Vue directive.
In the next 4 parts of this tutorial we’ll focus on each element of CRUD (Create, Read or Retrieve, Update and Delete) so in part 2 we’ll deal with Creating records to store in our Firebase Cloud Firestore.
Hope you can join me in Part 2 :)
You can download the completed code for this part of the tutorial on Github using the repo below and select the part1 folder. https://github.com/MancDev/VueFire
1 note
·
View note
Text
Angular & Firebase - Beyond the “Getting Started” tutorial
Querying & reading data
Developing for Firebase using Angular means getting our heads around some new concepts including using the AngularFire2 library which has a completely different API built around Observables. I wrote a post a while ago about developing for Firebase using Promises which are essential for developing with Firebase’s asynchronous nature and are much more manageable than callbacks. However with AngularFire2 we need to change how we think about dealing with querying Firebase to be more oriented around Observables whilst still utilising Promises to our advantage.
There’s a ton of tutorials and videos on getting started just using a call to the AngularFire2 list() function to get an Observable and unwrap it by putting an | async pipe on an *ngFor directive in your HTML. That’s very straightforward and great for getting going without getting your hands too dirty in the world of Observables. However that really only covers the most basic of use cases and in this post I’ll go beyond the getting started information to explain how to use AngularFire2 and Observables to carry out one of the most common Firebase programming tasks - querying Firebase for a value, wait for the answer, unpack and read that answer in code.
In explaining how to do this I won’t go heavily into Observables, I’ll just explain enough to get this example working. Once you work it out, it’s really not that different to working with the standard JS Firebase API & Promises.
Let’s say we’re querying a user’s node and we want to know whether they have access to a feature or not. We’ve got the key to the user’s node and we want to query it for HasAccess. Just like in the standard API we define a reference to the node we want. We’ll assume that we’ve injected an instance of the AngularFireDatabase class, called afdb, into the constructor of the component or service we’re using.
let query$ :Observable<any> query$ = this.afdb.object(”Users/” + userKey + “/HasAccess”, {preserveSnapshot :true} )
The object() function on AngularFireDatabase returns an Observable that we’ll use for querying the node we’ve declared and will, ultimately, return a single object. If we expected a list of data we’d use list() instead of object(). The second parameter is for optional settings such as passing query information. In this case we set preserveSnapshot to true so we get the full snapshot back when the Observable is run/unwrapped - exactly like we would in the standard API. Just as with setting a reference to a node in the standard API, declaring this reference doesn’t do anything just yet.
In the standard API we’d next call once() and hang a then() off the returning promise where we’d unwrap the data. We do the same process here but in a slightly different order. We use the map() function of the Observable to read and process the data (or transform it) in the way we want using a function as the parameter.
query$.map((snapshot) => { return snapshot.val() } )
It’s starting to look quite familiar - the snapshot is passed in and we use snapshot.val() to get at the actual data. In this case we’re directly reading the value of a specific node (HasAccess) so the value should either be true or false. Remember of course, at this point, we’re just setting things up and nothing has been set in motion just yet.
We’ll next chain another Observable method called first(). This grabs only the first value from the Observable once it’s unwrapped.
query$.map((snapshot) => { return snapshot.val() } ) .first()
We’re nearly there. However now we’ve got a little problem. Observables are designed to work on asynchronous streams of data that are constantly being emitted. Promises on the other hand carry out one task and return back control to you (via then() or await) when that task is complete. In this case we need to check the value to see if the user has access or not to decide what will happen next in our app and therefore we need to use a Promise. Fortunately Observables have a toPromise() function which returns a Promise containing the value we got from our Observable. When using Observables generally you would chain a subscribe() call to your Observable to set things in motion and this would continue running and emitting values. As we want a Promise back, the toPromise() call replaces subscribe(), sets the Observable in motion (in our case querying the database) and gives us back a Promise once the value of the query is returned (from first() ). As Promises are a one-time thing, the Observable passes us back the value through a Promise and quits it’s process (if using subscribe() you’d need to call unsubscribe() to quit the Observable).
As we get a Promise back with our result we can use then() or await to read the value and decide what we should do next. As I think async ... await is a beautiful thing I’ll use it to put it all together.
async DoesUserHaveAccess(userKey) :boolean {
let query$ :Observable<any> query$ = this.afdb.object(”Users/” + userKey + “/HasAccess”, {preserveSnapshot :true} ) return await query$.map((snapshot) => { return snapshot.val() } ) .first() .toPromise()
}
And that’s pretty much it. As you can see it’s really not that different and is quite simple although the documentation on how to do this is non-existent and so I hope this helps you out and saves you some time trying to achieve what is a fairly common database programming task.
0 notes
Text
Using Firebase Promises
With Firebase you don't have to worry about a back-end, an API, a database or any of that stuff and with the recent addition of Cloud Functions, we can now write JS server-side code as well - all with Firebase. It's quite simply beyond awesome. I've used Firebase for over 2 1/2 years for prototyping and proof-of-concepts and in 1 case as a small part of a production app but, due to the previous lack of server-side code capabilities never a full production app built entirely on Firebase. Upon attempting to do so it quickly became clear that learning asynchronous programming the Firebase way is a prerequisite and for any serious app you have to use Firebase Promises. You simply cannot avoid it.
In this post I'll cover what you need to know to use Firebase Promises to get you writing asynchronous database apps. Knowing Promises inside and out is useful but if you know how to get them to work within Firebase for a serious production app then you can expand your knowledge from that context rather than learning them as an abstract concept up-front and then trying to apply them to Firebase. None of the videos or articles I saw when I was learning Firebase Promises took this approach so this post is an aid for those who need to get going with Firebase's Promises without getting bogged down in the detail and will hopefully help clear the path a little for others who face the same problems I did.
Up until early 2016 Firebase relied entirely on callbacks. You registered an event to listen for changes on a particular node of your database and you provided it with a callback function that got executed initially and then when changes were made to the data. Of course it was asynchronous so you never knew when it would get fired but it was simple enough to isolate the callbacks from other parts of your code. If I needed to query other data whenever the callback was triggered I would just nest the code within the original callback function. I did get to about 4 levels of nesting with one prototype but it was fine, it did the job.
However when you start developing something more serious with Firebase things start to get a little tricky. Let's say you need to get some data and based on that value query something else and then based on that value query something else and then, based on that, ask the user for some input and so on. You can't simply nest all of your application logic in a callback function - it would get messy, ugly and well it's just not the done thing.
Fortunately early in 2016 Firebase released support for Promises. Promises save us from callback nesting hell by allowing us to chain asynchronous function calls together. With Promises you can run a function which makes an asynchronous query on a Firebase node and once it gets a result back from the database it then runs your next function, again with an async Firebase event, and when it gets that result back then it runs you next query and so on to allow a neat sequence of database queries all without nesting.
Promises is a big subject on it's own but here's my attempt to explain them as simply as I can - A Promise is a mechanism which allows you to make an asynchronous call and promises to give control back to you once it is either successful or unsuccessful. If it succeeds it enables you to read the outcome and deal with it accordingly when it's done. This makes it a perfect match for Firebase, to issue a query asynchronously with a promise to execute your code once the data is retrieved.
Having attempted it myself I would offer a word of advice. Do not attempt to develop a serious app in Firebase without using Promises. Just don't do it. Callbacks will drag you down as fast as quicksand into the 7 fires of development hell - you really don't want that. Promises make working with an asynchronous database almost (and I reiterate, almost) as easy as working synchronously with data. If you've chosen Firebase as your back-end (and it's a truly beautiful thing) then it's well worth any time you spend to learn it's Promises API.
Fortunately, with all of that said, Firebase makes it's pretty straightforward to convert from using the traditional callback events to promises :
Using Callbacks
dbRef.on('value', function(snapshot) { } ) Using Promises
dbRef.on('value').then(function(snapshot) {} )
So all we're doing is wrapping our callback function within then() of a Firebase Promise rather than as the second parameter of the on or once function call. If this is all we do then fine, it will work exactly as it did with the callback although we don’t really gain anything.
Please Note : As we're now in the post-ES5 world, I'll be using arrow function syntax from hereon in.
So what is this new-fangled then() function? It's part of Promises generally and is also baked into the Firebase promise API. Basically when you use the Promise API, the then() function gets executed if the call to the Firebase on() or once() functions were successful. We can also use a different function if there was an error with the call but we won't cover it in this post.
However Promises are only really useful when they are chained. Let's say we need to look-up a user's data. We've got their email address from the log-in process and we need to find their record from the database and when we've got it we can then work out where their actual information is located in the database so we can query for their data. The image below shows the structure of the data.
First of all we have to run a query to find the user record. As we only want to run it one time to get a result and not continue to listen for changes we'll use the once() event function.
query = dbRef.child('users').orderByChild('email').equalTo(loginEmail).limitToFirst(1);
query.once('value').then(snapshot => {} )
This post isn’t about Firebase queries but I'll briefly explain what we're doing. We're looking on the users node child('users') and searching by the email property within that node orderByChild('email'), looking for a record with an email property equal to the login email equalTo(loginEmail) and making sure we only get the first record it finds limitToFirst(1). We're using the 'value' event rather than the 'child_added' event as we want it to trigger even if no results are returned from the query.
In the callback function we return the unique key from the user's record we find :
query.once('value').then(snapshot => { let userKey = ''; if (snapshot) { snaphot.forEach(childSnap => { userKey = childSnap.key; }) } return userKey; });
As we're using the 'value' event on the 'users' node, Firebase will point us to that node and we'll have to dig into it using the forEach function to get at the actual document that's been found and grab it's key. The key will then give us the id of the node we need to point to get their main data.
Our next step is to use the key that's being returned and run another query to get the user's data. However If we don't use promises we have no way of knowing when the result of our first query will come back from the database so we won't know when to call our second query. To set up our second query to only trigger when we know that the first query has returned a value we place the second query in another Firebase Promise then() function.
This is where it can get a little confusing. What does the then() function actually return and how can we get the key we return from our first callback into the second query?
The key thing to remember here is then() returns yet another Promise object. Therefore this new returned Promise provides it's own then() function that we can call to chain to the first promise. In this returned Promise we can use then() to include our second query which will only get called when the first query is done ie
query.once('value').then(snapshot => { let userKey = ''; if (snapshot) { snaphot.forEach(childSnap => { userKey = childSnap.key; }) } return userKey; }) .then();
All well and good but how can we get at the value of userKey returned out of the callback in our first promise. The answer to this is when we return a value from a callback, it is passed back to the then() function that called it. then() passes your return value from your callback to the new Promise it's returning. The new Promise then passes that value into the parameters of it's own then() function.
query.once('value').then(snapshot => { let userKey = ''; if (snapshot) { snaphot.forEach(childSnap => { userKey = childSnap.key; }) } return userKey; }) .then(userKey => { });
From within the callback function that we declare in the new then() we can read the userKey value and query the database accordingly in the knowledge that the first query has been run.
In the next query we use the userKey passed in by the second Promise to get at the database node we want, retrieve the user's data and populate it into an array eg :
.then(userKey => { let userDataRef = dbRef.child('usersdata').child(userKey); return userDataRef.on('value').then(snapshot => { let userDataList = []; if (snapshot) { snapshot.forEach(childSnap => { userDataList.push({ /*store data properties*/ }); }); } return userDataList; } });
Notice that we return the result of the new query then() function call . As we explained earlier this is another Promise so by returning a Promise from this we can chain yet another promise to call then() and add another step in the chain until the whole process is completed.
If we flatten the code we can see how values are returned from one step in the promise to the next :
query.once('value').then(snapshot => { return value1 } ) ________________________________| | .then(value1 => { return then(snapshot => { return value2 } ) } ) ____________________________________| | .then(value2 => { return then(snapshot => { return value3 } ) } )
Once you've got all of the values you need out of the data you could add another step in the promise chain to render the results to the screen etc. This is how we work with queries in Firebase particularly where multi-step queries are required.
One other important thing is to remember that other code in sequence after the promise will continue to be executed without deference to the promise chain. Therefore don't assume that promises will stop the execution of your entire app completely until a particular promise chain is done. Only the steps in the promise chain will wait and execute after each step, everything else will run in sequence in your code. Therefore if you need the chain to complete before your app moves on you should add a function call on the very last then() in the chain to continue running your app.
The wrong way to handle asynchronous execution
function GetUserData() { let userData = []; //run promise chain to perform multi-step query .then( dbQueryVals => { //last promise in the chain to get executed userData = dbQueryVals; } return userData; } userData = GetUserData(); RunApp(userData); //this is outside of the promise & so gets executed //immediately regardless of whether the queries in //the promise chain have been resolved
The right way to handle asynchronous execution
function GetUserData() { //run promise chain to perform multi-step query then(userData => { //last promise in the chain to get executed RunApp(userData); //runs the app when everything is done }); //and queries are resolved }
Alternatively you could make the GetUserData() function create and return it's own promise and chain the call to RunApp() within a then() returned from that promise. That's getting more into general promises for the scope of this post but, now you know how Firebase promises work, you can expand your knowledge of how Promises work.
If you're a budding Firebase developer or, like me, have used it before but now getting serious with building full production apps on it, I hope this helps gives you the info you need to deal with the async ways of Firebase. Good luck :)
0 notes
Text
Escaping from Delphi
Part One - Is Delphi Dead?
“Delphi is Dead!” - I remember when I first heard or rather read that statement. It was back in 2001 shortly after MS has released .NET and their "Java-killer” C# language as well as a major IDE in the form of Visual Studio. The author of the article explained how C# was a better Delphi for the all-new .NET framework and was stealing it’s thunder. I had been intrigued by C# and even had the beta in 2000 but the statement of Delphi being dead seemed a little premature considering Delphi’s popularity and superiority over MS’ previous flagship development system, Visual Basic. At the time I was deep in a multi-year project with Delphi and we couldn’t simply just stop to jump on Anders’ new bandwagon.
The rise in popularity of C# in the early 2000s was nothing short of astonishing and it’s impact on the Delphi ecosystem was almost immediate and devastating. It quite simply took away virtually every competitive advantage Delphi had almost overnight. C# never quite became the Java-killer MS had hoped it would be but it quickly started to become the Delphi-killer. Instead of responding to the threat of C# and.NET, Borland developed a weird obsession with getting Delphi onto Linux with a doomed product called Kylix which no-one cared about. All too late Borland woke up to finally release a .NET version of Delphi at the beginning of 2004 but it was horrendously buggy, with an attempt to fit Delphi’s VCL into .NET’s API and it fell a long way short of matching up to the MS offering. Instead of halting the relentless exodus of developers from Delphi to C#, this failure accelerated it.
The “Delphi is Dead” mantra was further fuelled as C# and .NET exploded and MS drove it forward relentlessly while Borland flailed in their innovation. They back-tracked after the disaster of Delphi 8 on their decision to focus on .NET and instead released 2 half-hearted updates in the mid-noughties which included both VCL/native and their half-baked .NET implementation. Worst of all they completely redesigned their IDE which, while admittedly superior, removed their advantage of familiarity for existing developers. If you were going to have to learn to work with a new IDE then why not learn Visual Studio like the rest of the world seemed to be doing?
After Delphi 8’s disastrous .NET attempt, a small company named RemObjects saw the situation as an opportunity to create a “proper” Delphi for .NET which they called Chrome and later renamed to Oxygene. Unimpeded by any need for backward compatibility with Delphi’s VCL they designed an implementation which was potentially another dagger in Delphi’s heart. It’s important to note Delphi was never a language in and of itself, instead it was always more of a brand for a product which encompassed an OOP version of the Pascal language (descended from Borland’s famed Turbo Pascal and referred to as Object Pascal), it’s own IDE and the VCL which was a component abstraction layer over the Win32 API. With Oxygene, RemObjects focussed almost entirely on the language part to deliver a true Object Pascal for .NET, they utilised Visual Studio as their IDE and there was simply no need for a VCL-style abstraction layer as they interfaced directly with the .NET framework rather than Win32.
There was a brief moment of hope for Delphi in 2006 when Borland spun it’s developer tools division off into it’s own autonomous company called CodeGear. The buzz this generated was phenomenal and those who’d been hoping for a revival, like myself, were elated. And at that point there was a real chance it could be turned around. Delphi 2007 was the first “proper” release since Delphi 7, 4 years earlier, and the majority flocked to it. With CodeGear they finally figured out that their only real competitive advantage over C# was that Delphi produced a native, self-contained executable that was perfect for “shrink-wrapped” products that didn’t rely on end-users having a specific version of the .NET framework installed for your software to work - this was the single reason I stuck with Delphi for so long even for new projects (although it no longer applies). It all looked extremely hopeful. Sadly it was a false dawn. It was all done to create a fresh buzz to attract a buyer for the developer tools business. Borland had seen the future and it was the commoditisation of developer languages and they wanted to offload it before it became a drain on their other more profitable businesses. In the end CodeGear was acquired not by Microsoft, Oracle or IBM but by a company nobody had heard of called Embarcadero. A sign if ever there was one of where Delphi now stood in the food chain of developer tools. The days of punching above it’s weight to see off Microsoft and haling it as the “VB Killer” were now just a distant memory.
After Delphi’s acquisition by Embarcadero in 2008, the new owners dropped Delphi for .NET which helped to further alienate large numbers of developers who had, for better or worse, adopted it. As a replacement they wisely licensed Oxygene from RemObjects as their .NET option. They branded it Delphi Prism which was a bit of a joke seeing as it had absolutely nothing to do with Delphi except they shared a core language in Pascal. With this Delphi became a strictly Win32 development system. While Delphi’s Pascal implementation stagnated, the Oxygene/Delphi Prism implementation got more advanced with relentless development, becoming a Pascal++.
As we entered the teenies, Delphi was on it’s last legs in terms of developer numbers and while Embarcadero continued to claim 3 million developers it was fairly obvious that these were old numbers or represented a mass of developers who had moved on but need to maintain older apps. The number of 3rd party component developers was down significantly and the job market was in the gutter, Delphi shops couldn’t find skilled developers and Delphi developers were finding their skills unwanted. A new generation of developers were coming through who had never even heard of Delphi never mind actually used it. It seemed only those who were already working on long-term projects, had existing apps to maintain or had such a deep investment in their Delphi skills that they didn’t want to switch seemed to be staying with it.
In 2012 Embarcadero made one last throw of the dice to reboot Delphi and attempt to reignite the fire necessary for any development tool - new developer take-up. The focus would be on cross-platform and on mobile. It would be iOS, Android and MacOSX. But with the VCL wrapped around Win32 how could they achieve this? Their answer was something called FireMonkey. FireMonkey would not utilise native frameworks on any platform such as .NET or Cocoa. Instead it would draw the components to look native on each platform. It was a bold attempt and it was worth a shot but it was almost universally hated and took Delphi in a direction that pushed even diehards to the end of their tether. Most Delphi apps would require huge rewrites or massive refactoring projects to get on this new cross-platform tech. It was as buggy as hell and massively expensive. RemObjects had made their Oxygene compiler cross-platform to now work across .NET, Java (inc Android) and Cocoa (for iOS & MacOSX) and so rather than complement Delphi it was now a potential competitor. So Embarcadero shot the fox in it’s new hen-house by discontinuing their license agreement with the small developer tools company. There was now no avenue to .NET through Delphi. Instead Embarcadero just shouted even louder the same old mantra we’d heard since the early 2000s “native is best” - an argument which had lost it’s relevancy long ago.
Embarcadero limped on like this for the next few years and several more releases, focussing on getting existing developers to pay substantial annual subscription fees, realising that the goose of mass new developer uptake was finally cooked. At the end of 2015 Embarcadero was acquired by Idera. Within 6 months they had let almost all of the Delphi engineering and technical teams go. People who had been there for decades through all of the ups and downs of Delphi were suddenly gone. Even the seemingly omnipresent Delphi acolyte David Intersimone (David I) has now gone - a man whose propensity for cheerfully towing the corporate line of whoever owned Delphi seemed to know no bounds.
The final nail in Delphi’s coffin could well have come, in the end, from Microsoft. They released a community edition of Visual Studio completely free. Then they acquired Xamarin which was competing (and winning hands-down) with Delphi for the mobile development space. While Xamarin maintained high prices, Delphi could justify it’s own prices. But Microsoft incorporated Xamarin into Visual Studio making it completely free to all. This means C# can reach virtually any platform, including web, based on .NET and Mono and developers can use a world-class IDE - all for free. The case for adopting Delphi for new projects is pretty much non-existent today and the chances of start-ups or new developers adopting Delphi, with it’s esoteric & stagnant language and it’s high adoption and maintenance costs, are slim to none.
The question of whether Delphi is truly dead has been hotly debated since the early 2000s. Many have switched camps from the No to the Yes side and vice versa over the years. I count myself among them. But is it truly dead? Those in the No camp will point to the fact that people have been saying it is dead since 2001 as a sign the naysayers have always got it wrong however they still have to admit that it has been haemorrhaging developers in increasing numbers since 2001, there’s no doubt about that. But no, Delphi isn’t dead but only in the same way as PowerBuilder, COBOL and xBase aren’t dead - they are however in a coma on life support and are never realistically going to recover. It now seems only a matter of time before the new owners realise that high-priced proprietary development languages are on their way out. When they finally conclude that there’s no longer any more dollars to be sucked out of it they’ll pull the plug in a heartbeat.
For the future of Delphi developers, their apps, their careers and their businesses we cannot afford to wait for the life support to be switched off. I left Delphi back in 2012. Having used every Delphi version including 1, I never moved beyond the 2007 edition having never seen anything worthwhile in any of the very expensive Embarcadero updates. No developer tool has ever recovered to prominence from such a low base and listening to the people who remain at Embarcadero, they’re still talking like it was 10 years ago and refusing to acknowledge present reality. All of the stats show that Delphi is already in the grave in the US and in most of Europe, especially the UK. It appears that usage in other parts of the world are keeping it afloat. Reading an article from their new CEO it seems a resurrection in the US is so far-fetched it’s not even on their radar. No developer, especially in the US, Western Europe and the UK can stake their future on a development system that is effectively a zombie with virtually no developers West of Warsaw or north of El Paso and no technical team to drive it forward.
That of course is not to say that there isn’t millions of lines of code in products developed with Delphi, I still have an important, revenue generating, app in Delphi that I still maintain and cannot afford to scrap or rewrite. So where do developers like me with huge codebases and commercial apps developed in Delphi go now?
I laced the story so far with mentions of RemObjects because they are an important part of where we can go now. They are offering a way to move on from Delphi without rewrites and without huge expense. They are offering us the ability to Escape from Delphi in a way which, they claim, means we can do it steadily, develop new features in more innovative tech and in a way we choose. And we don’t need to be stuck with Pascal either if we don’t want to be (personally I was never enamoured with Pascal and for me it was something I had to use to get at Delphi’s other benefits so the fact I can move my Delphi app forward in another language is a real bonus).
In Part Two of this post I’ll review the components of RemObject's “Escape from Delphi” offer package and show how they can help you breathe new life into legacy apps without the “all-or-nothing” approach you’d normally expect when moving an app to different technologies and platforms.
*I do not have any affiliation to Remobjects nor do I stand to gain financially or in any other way directly from reviewing and/or advocating their tools.
1 note
·
View note
Text
Build a realtime dashboard with Firebase & Chartjs - Part 2
In this post I’ll continue on from the previous article and complete our realtime dashboard app. Last time we got all of our charts working with static data and finished off the first chart, a line graph showing sales and profit by month, by getting the data from Firebase and populating the chart. Any change you make to the Firebase sales data will be reflected in realtime to our line chart.
So let’s wrap things up with our dashboard by retrieving the data from Firebase to populate the final two charts, Sales By Salesperson and Sales By Region. As mentioned in the previous post, structuring your data in Firebase is really important for retrieval of data that won’t require too much manipulation on the client. However as Firebase is a NoSQL object store rather than a relational database with tables, the structure of data that works with one type of retrieval may not necessarily work too well with another and it is common in NoSQL to duplicate the data in different formats and sections of your database to retrieve the data specifically you way you want it. In our case the way we’ve structured the data to easily handle the Sales By Month chart isn’t ideal for retrieving data for the other 2 charts. For example if we wanted to sort the order of the bars in the Sales By Person chart into salesperson order we couldn’t do it with our current data structure due to the way we’ve nested the sales data objects inside different nodes for each month. However I don’t want to overcomplicate things here so we’ll stick with the current data structure and code the manipulation of data that we need to populate the last 2 charts and in this case it isn’t too much hassle to do.
Let’s start with the Sales By Sales Person bar chart. To manipulate the data from our structure in Firebase we need to iterate through the objects and add up the total of sales and profit for each sales person. Unlike the first chart where we could rely on the order of data, by month, for this chart we need to do a bit of checking to ensure we sum up the right figures for the right salesperson. As we’re working with the same event listener as the first chart we can put our code in the same listener function as we created in the first chart.
The listing below shows the code we need to calculate the data for our Sales By Salesperson bar chart. The changes from the previous listing are highlighted in bold. Notice we added some new variables and we move the assignment of the canvas context into our listener - this ensures that our charts are properly refreshed each time they are updated - while you should remove the context assignment from the beginning of your main IFEE function we still need to declare the variables as we did before just using var (we’ll include a full listing of the code of the end of the post). Notice we now reset all our data arrays each time the listener is triggered as well.
var salesByMonthListener = function salesByMonthListener(snapshot) {
var sales; var profit; var salesPerson; var region; var regionObject;
ctxSalesByMonth = $("#salesByMonth").get(0).getContext("2d"); ctxSalesByPerson = $("#salesByPerson").get(0).getContext("2d"); ctxSalesByRegion = $("#salesByRegion").get(0).getContext("2d");
dataSalesByMonth.labels = []; dataSalesByPerson.labels = []; dataSalesByRegion = [];
dataSalesByMonth.datasets[0].data = []; dataSalesByMonth.datasets[1].data = [];
dataSalesByPerson.datasets[0].data = []; dataSalesByPerson.datasets[1].data = [];
snapshot.forEach(function(salesMonthSnapshot) {
sales = 0; profit = 0;
thisMonthName = salesMonthSnapshot.val().month; dataSalesByMonth.labels.push(thisMonthName);
salesMonthSnapshot.forEach(function(salesSnapshot) {
if (salesSnapshot.hasChildren()) { var thisSales = salesSnapshot.val().sales; var thisProfit = salesSnapshot.val().profit;
sales += thisSales; profit += thisProfit;
salesPerson = salesSnapshot.val().salesperson;
idxSalesPerson = dataSalesByPerson.labels.indexOf(salesPerson);
if (idxSalesPerson === -1) { idxSalesPerson = (dataSalesByPerson.labels.push(salesPerson) - 1); dataSalesByPerson.datasets[0].data.push(0); dataSalesByPerson.datasets[1].data.push(0); }
dataSalesByPerson.datasets[0].data[idxSalesPerson] += thisSales; dataSalesByPerson.datasets[1].data[idxSalesPerson] += thisProfit;
}
});
dataSalesByMonth.datasets[0].data.push(sales); dataSalesByMonth.datasets[1].data.push(profit);
})
lineSalesByMonth = {}; lineSalesByMonth = new Chart(ctxSalesByMonth).Line(dataSalesByMonth); $("#legendSBM").html(lineSalesByMonth.generateLegend());
barSalesByPerson = {}; barSalesByPerson = new Chart(ctxSalesByPerson).Bar(dataSalesByPerson); $("#legendSBP").html(barSalesByPerson.generateLegend());
};
We’re assigning the sales and profit properties of each sales record object into variables, thisSales and thisProfit. We’re then getting the salesPerson name and checking the labels array of the chart’s data object to see if it already has a salesperson with that name. If we don’t (which we can tell if indexOf returns -1) we add the salesperson’s name to the labels array (and get it’s index by using the new length of the array returned by the push() method and decrementing by 1 as arrays use zero-based indexes) and set the data array for both the first dataset (sales) and second dataset (profit) to zero. We then increase the sales and profit values in the respective datasets’ data arrays which will sum up totals for each salesperson with each iteration of the sales data. At the bottom of our listener function we create the bar chart and pass in the data object we’ve populated. This is pretty much all we need to do get the Sales by Salesperson bar chart working.
Now for our last chart, Sales By Region. As this is a pie chart it’s a little bit trickier than the previous bar chart. As covered in the previous post, pie charts don’t have datasets with their labels and values held in neat arrays, instead each slice of data in our pie is represented by an object in a data array. Each “slice" object has a label and a value. To check to see if we need to add a new region object to our pie data array we can’t simply use indexOf, we need to have another function which will iterate over our object array and see if we already have an object with a label property for each region to check whether or not we need to create a new object or use an existing one. Here’s our region finder function which you should place below the end of the main listener function :
var findRegion = function findRegion(region, regionArray) { var result = -1; for (var count = 0; count < regionArray.length; count++ ) { if (regionArray[count].label === region) { result = count; break; } } return result; }
This isn't doing anything complicated but it does the job of iterating over our array of region data objects and checks if the label we pass to it is already has a data object or not. As indexOf does, if it can’t find a region it will pass back -1.
Because we’re operating on any number of “slices” or regions and each region needs it own colour in the pie we need to decide how we’re going to assign colours. There are 2 ways to do this, the first is to calculate the colours by assigning random numbers or calculating each of the red, green and blue elements in our colours for each region. The second is to setup a list or an array of pre-defined colours. As we know we’ll only have a limited number of regions in this demo we’ll go for the 2nd option. Add this code to the main body of our IFEE function directly under where we declare the data object variables and before our listener function. Notice we’re using an array of objects each containing properties for the rgb colours we’ll be assigning to our region slices :
var dataSalesByRegion = [];
var pieColors = [{r: 40, g:80, b:220}, {r: 220, g: 100, b: 92}, {r: 218, g: 130, b: 39}, {r: 230, g: 150, b: 200}, {r: 200, g: 240, b: 50}, {r: 40, g: 180, b: 120}, {r: 180, g: 120, b: 40}, {r: 100, g:240, b:80}];
Back in our listener function, immediately before the close of salesSnapshot.hasChildren() conditional, add the following code .
region = salesSnapshot.val().region; idxRegion = findRegion(region, dataSalesByRegion);
if (idxRegion === -1) { nxR = dataSalesByRegion.length; if (nxR > pieColors.length-1) { nxR = 0; }
regionObject = {label: region, color: "rgba(" + pieColors[nxR].r + ", " + pieColors[nxR].g + ", " + pieColors[nxR].b + ", 0.5)”, highlight: "rgba(" + pieColors[nxR].r + ", " + pieColors[nxR].g + ", " + pieColors[nxR].b + ", 1)”,
value: 0
} idxRegion = (dataSalesByRegion.push(regionObject) - 1); }
dataSalesByRegion[idxRegion].value += thisSales;
This gets the region from Firebase, checks to see if a data object has already been created for it, using the function explained above. If not it creates a new regionObject containing the region name, assigning it’s colours from the pieColors array we declared earlier, setting an initial value of zero and getting the new object’s index. Notice that we do a check so if we run out of colours from the colour array , we reset the colour count (nxR) to zero to start using the same colours again. Finally we increase the value of sales for the current region from the value (thisSales) grabbed from our Firebase.
All we need to do now is generate our pie chart and it’s legend at the end of our listener function, similar to how we did it for the previous charts :
pieSalesByRegion = {}; pieSalesByRegion =new Chart(ctxSalesByRegion).Pie(dataSalesByRegion); $("#legendSBR").html(pieSalesByRegion.generateLegend());
That’s it we’re done! Go check your app by changing data in your Firebase dashboard and see the changes reflected in all of the charts in your dashboard in realtime.
There was nothing complex in this app, just a basic knowledge of the Firebase API, ChartJS’ API and data formats and working out how to turn the structure of your data retrieved from Firebase into a format that ChartJS can use. Hopefully it’s given you a little inspiration on how straightforward it can be to build realtime charting into your own apps.
PS : Here’s the full and final listing of the code. You can also grab it from GitHub.
(function() {
var firebaseRef; var ctxSalesByMonth; var ctxSalesByPerson; var ctxSalesByRegion;
var dataSalesByMonth = { labels : [], datasets: [ { label: "Total Sales", fillColor: "rgba(67, 214, 92, 0.5)", strokeColor: "rgba(67, 214, 92, 1)", pointColor: "rgba(67, 214, 92,1)", pointStrokeColor: "#fff", pointHighlightFill: "#fff", pointHighlightStroke: "rgba(67, 214, 92,1)", data: [] }, { label: "Gross Profit", fillColor: "rgba(218, 233, 39, 0.5)", strokeColor: "rgba(218, 233, 39, 1)", pointColor: "rgba(218, 233, 39,1)", pointStrokeColor: "#fff", pointHighlightFill: "#fff", pointHighlightStroke: "rgba(218, 233, 39,1)", data: [] } ]};
var dataSalesByPerson = { labels : [], datasets: [ { label: "Total Sales", fillColor: "rgba(67, 214, 92, 0.5)", strokeColor: "rgba(67, 214, 92, 1)", pointColor: "rgba(67, 214, 92,1)", pointStrokeColor: "#fff", pointHighlightFill: "#fff", pointHighlightStroke: "rgba(67, 214, 92,1)", data: [] }, { label: "Gross Profit", fillColor: "rgba(218, 233, 39, 0.5)", strokeColor: "rgba(218, 233, 39, 1)", pointColor: "rgba(218, 233, 39,1)", pointStrokeColor: "#fff", pointHighlightFill: "#fff", pointHighlightStroke: "rgba(218, 233, 39,1)", data: [] } ]};
var dataSalesByRegion = [];
var pieColors = [{r: 40, g:80, b:220}, {r: 220, g: 100, b: 92}, {r: 218, g: 130, b: 39}, {r: 230, g: 150, b: 200}, {r: 200, g: 240, b: 50}, {r: 40, g: 180, b: 120}, {r: 180, g: 120, b: 40}, {r: 100, g:240, b:80}];
var lineSalesByMonth; var barSalesByPerson; var pieSalesByRegion;
$(document).ready(function() {
firebaseRef = new Firebase("https://scorching-fire-224.firebaseio.com");
var salesRef = firebaseRef.child("sales");
salesRef.on('value', salesByMonthListener);
});
var salesByMonthListener = function salesByMonthListener(snapshot) {
var sales; var profit; var salesPerson; var region; var regionObject;
ctxSalesByMonth = $("#salesByMonth").get(0).getContext("2d"); ctxSalesByPerson = $("#salesByPerson").get(0).getContext("2d"); ctxSalesByRegion = $("#salesByRegion").get(0).getContext("2d");
dataSalesByMonth.labels = []; dataSalesByPerson.labels = []; dataSalesByRegion = [];
dataSalesByMonth.datasets[0].data = []; dataSalesByMonth.datasets[1].data = [];
dataSalesByPerson.datasets[0].data = []; dataSalesByPerson.datasets[1].data = [];
snapshot.forEach(function(salesMonthSnapshot) { sales = 0; profit = 0;
thisMonthName = salesMonthSnapshot.val().month; dataSalesByMonth.labels.push(thisMonthName);
salesMonthSnapshot.forEach(function(salesSnapshot) { if (salesSnapshot.hasChildren()) { var thisSales = salesSnapshot.val().sales; var thisProfit = salesSnapshot.val().profit;
sales += thisSales; profit += thisProfit;
salesPerson = salesSnapshot.val().salesperson; idxSalesPerson = dataSalesByPerson.labels.indexOf(salesPerson); if (idxSalesPerson === -1) { idxSalesPerson = (dataSalesByPerson.labels.push(salesPerson) - 1); dataSalesByPerson.datasets[0].data.push(0); dataSalesByPerson.datasets[1].data.push(0); } dataSalesByPerson.datasets[0].data[idxSalesPerson] += thisSales; dataSalesByPerson.datasets[1].data[idxSalesPerson] += thisProfit;
region = salesSnapshot.val().region; idxRegion = findRegion(region, dataSalesByRegion); if (idxRegion === -1) { nxR = dataSalesByRegion.length; if (nxR > pieColors.length-1) { nxR = 0; } regionObject = { label: region, color: "rgba(" + pieColors[nxR].r + ", " + pieColors[nxR].g + ", " + pieColors[nxR].b + ", 0.5)", highlight: "rgba(" + pieColors[nxR].r + ", " + pieColors[nxR].g + ", " + pieColors[nxR].b + ", 1)", value: 0 } idxRegion = (dataSalesByRegion.push(regionObject) - 1); } dataSalesByRegion[idxRegion].value += thisSales; }
});
dataSalesByMonth.datasets[0].data.push(sales); dataSalesByMonth.datasets[1].data.push(profit);
})
lineSalesByMonth = {}; lineSalesByMonth = new Chart(ctxSalesByMonth).Line(dataSalesByMonth); $("#legendSBM").html(lineSalesByMonth.generateLegend());
barSalesByPerson = {}; barSalesByPerson = new Chart(ctxSalesByPerson).Bar(dataSalesByPerson); $("#legendSBP").html(barSalesByPerson.generateLegend());
pieSalesByRegion = {}; pieSalesByRegion = new Chart(ctxSalesByRegion).Doughnut(dataSalesByRegion); $("#legendSBR").html(pieSalesByRegion.generateLegend());
};
var findRegion = function findRegion(region, regionArray) { var result = -1; for (var count = 0; count < regionArray.length; count++ ) { if (regionArray[count].label === region) { result = count; break; } } return result; }
})();
0 notes
Text
Building a realtime dashboard with Firebase and Chartjs
Firebase is the perfect back-end for rapid prototyping and for building realtime production apps and it’s realtime capabilities make it the ideal way for building dashboard apps. Building a dashboard app with charts that don’t cost you a bean is where ChartJS comes in.
ChartJS lets you include great looking charts in your app, from line graphs to bar and pie charts and lets you customise them really easily. Now if you want really, really, really spectacular 3D charts in your app then you’ll likely need to go and pay a hefty sum for a charting library but if it’s a prototype you’re building or you don’t wanna a spend any dough then ChartJS is more than capable of giving you glorious looking charts. Working together with Firebase, you can get charts updating in realtime from a cloud datastore enabling you to build a dynamic dashboard in less than a few of dozen lines of Javascript code. Sound cool? Let’s show you how it’s done.
Setup with a skeleton HTML page called index.html as shown below. We’ll be keeping things really simple in this tutorial, enabling Firebase and ChartJS to take centre stage so we’ll only be using jQuery rather than a major framework. I really like Semantic-UI as an alternative to Bootstrap as the CSS and layout library so we’ll use their CDN as well. Everything we need is shown below:
<!DOCTYPE html>
<html>
<head>
<title>FireChart</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.1.8/semantic.min.css”> <link rel="stylesheet" href="firechart.css">
</head>
<body style=“margin:20px">
<script src="http://code.jquery.com/jquery-1.12.2.min.js" integrity="sha256-lZFHibXzMHo3GGeehn1hudTAP3Sc0uKXBXAzHX1sjtk=" crossorigin="anonymous"></script> <script src="https://cdn.firebase.com/js/client/2.4.2/firebase.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/1.0.2/Chart.min.j</script> <script src="firechart.js"></script>
</body>
</html>
Notice we reference a firechart.css and a firechart.js. We’ll be creating these files shortly.
Before you get going you’ll need to set yourself up with a Firebase if you don’t have one already. It’s completely free and really easy to do. Just go to firebase.com and click Signup with Google, follow the destructions and once you’re setup you’ll get a unique Firebase Url ending in firebaseio.com. Copy that as you’ll need it in your code to reference your Firebase.
The first part of this tutorial will all be about getting our charts setup. We’ll then move on to getting the data from Firebase to turn those charts into a dynamic, realtime dashboard.
Let’s say our Dashboard is going to have 3 charts based on sales data for the past six months. This sales data contains the total of sales and gross profit for each month, each salesperson and region. In the first chart we’ll show a line graph of the total sales and gross profit for each of the six months. On the second graph we’ll show a bar chart telling us the breakdown of sales per salesperson for the whole period. The third area on our dashboard will show a pie or a doughnut chart for the whole period by region.
We’ll start but laying out our HTML page which is surprisingly simple thanks to ChartJS. We’ll use Semantic-UI’s grid layout to keep our charts in order and everything responsive. We’ll put a <h1> tag as our header and some <div> elements which implement Semantic’s grid system. We’ll be having a layout with 3 areas across, one for each chart. Semantic’s grid system uses 16 columns across so we’ll set each of our areas to be 5 columns wide for a total of 15 leaving one over for padding. We’ll create a div to surround everything and we can do all of this simply by specifying some Semantic classes on our <div> container elements. The HTML below should be placed immediately below the opening <body> tag :
<h1>Firechart Dashboard</h1>
<div class="ui grid">
<div class="five wide column">
</div>
<div class="five wide column">
</div>
<div class="five wide column">
</div>
</div>
Each of our charts will be placed within each grid column area. The actual charts will not be defined in HTML but in Javascript however we have to tell the page where they will be drawn. To do this we need to define <canvas> elements in our HTML and give each of them a specific id which we’ll use to reference them in our Javascript :
<div class="ui grid">
<div class="five wide column"> <canvas id=“salesByMonth”></canvas> </div>
<div class="five wide column"> <canvas id=“salesByPerson”></canvas> </div>
<div class="five wide column"> <canvas id=“salesByRegion”></canvas> </div>
</div>
That’s pretty much all we need for the HTML. Next create a new file in the same folder as your index.html called firechart.css. This will include some important classes for styling our charts as shown below :
canvas{ width: 100% !important; max-width: 1200px; height: auto !important; max-height:600px; }
.chartLegend li span{ display: inline-block; width: 12px; height: 12px; margin-right: 5px; }
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
.chartLegend li { display: inline; padding-right: 8px; }
.bar-legend li { list-style: none; }
.line-legend li { list-style: none; }
.pie-legend li { list-style: none; }
.align-center { text-align: center; }
The classes above the squiggly line are really important to our chart layout and you should definitely add them. The classes beneath the line are more my preferences so you can leave them or change them if you want. We’ll explain what’s happening here later but we really want to get our charts working with our Javascript so we’ll come back to it later.
Create another new file in the same location called firechart.js. This is where we’ll be coding our logic.
As explained earlier we’ve only placed a canvas element on our page. To hook a chart up to a canvas element we’ll need to define a context to the canvas first. We’ll define a context for each of the 3 canvas elements and set them to 2d as that’s the type of display used by ChartJS. We’ll use jQuery selectors to reference each of the canvas elements :
(function() {
var ctxSalesByMonth = $(“salesByMonth”).get(0).getContext(“2d”); var ctxSalesByPerson = $(“salesByPerson”).get(0).getContext(“2d”); var ctxSalesByRegion = $(“salesByRegion”).get(0).getContext(“2d”);
})();
Now we can create our charts by simply newing up a Chart object and passing in the relevant context. We then chain on a function call to the type of chart that we want to create. In our example each of our charts uses a different graph type :
var chartSalesByMonth = new Chart(ctxSalesByMonth).Line(); var chartSalesByPerson = new Chart(ctxSalesByPerson).Bar(); var chartSalesByRegion = new Chart(ctxSalesByRegion).Pie();
We now need to actually pass the data that we want our chart to use along with display information such as labels and colours. To do this we need to setup an object containing everything we want to display. Let’s start with our Sales By Month line chart. Here’s the sample data object we use which we’ll insert above the chart declarations :
var dataSalesByMonth = { labels : ["Jan", "Feb", "Mar", "Apr", "May", "Jun"], datasets: [ { label: "Total Sales", fillColor: "rgba(67, 214, 92, 0.5)", strokeColor: "rgba(67, 214, 92, 1)", pointColor: "rgba(67, 214, 92,1)", pointStrokeColor: "#fff", pointHighlightFill: "#fff", pointHighlightStroke: "rgba(67, 214, 92,1)", data: [20000, 25000, 30000, 22000, 28000, 34000] } ]};
The labels property of our data object is an array with each value representing a point in the line graph. In this example we’re using the first 6 months of the year. datasets is another array with each element an object containing information for a specific range of data. In this case, as it’s a line chart, each dataset will represent a line in the chart. In the example we only have a single dataset so it’s not too important at this point but we will be adding another one later.
Each dataset has a label property, useful for when displaying a legend for the chart. We have a series of colour settings which are not really important here but you can play around with these settings yourself to get them looking pretty. What we’re interested in is the data property which is our actual data for the chart. Each element in the data array corresponds to an element in the labels array. So in the example above Jan is 20000, Feb is 25000 and so on. Obviously later we’ll make this data dynamic by retrieving it from our Firebase instead but for now this static data will do to get us up and running.
To assign this data object to our chart we simply pass it as a parameter to our Line() function as below :
var chartSalesByMonth = new Chart(ctxSalesByMonth).Line(dataSalesByMonth);
For the next graph, which will be a Bar chart, we’re going to display the total sales for each sales person. In this chart we want to display a bar for the total sales of each salesperson and alongside a bar for the gross profit for each salesperson. As we’re showing two pieces of data (bars) for each salesperson we’ll need two datasets :
var dataSalesByPerson = { labels : ["Bill", "Bob", "Jane", "Judy", "Max", "Holly"], datasets: [ { label: "Total Sales", fillColor: "rgba(67, 214, 92, 0.5)", strokeColor: "rgba(67, 214, 92, 1)", pointColor: "rgba(67, 214, 92,1)", pointStrokeColor: "#fff", pointHighlightFill: "#fff", pointHighlightStroke: "rgba(67, 214, 92,1)", data: [20000, 25000, 30000, 22000, 28000, 34000] }, { label: "Gross Profit", fillColor: "rgba(218, 233, 39, 0.5)", strokeColor: "rgba(218, 233, 39, 1)", pointColor: "rgba(218, 233, 39,1)", pointStrokeColor: "#fff", pointHighlightFill: "#fff", pointHighlightStroke: "rgba(218, 233, 39,1)", data: [10000, 12500, 15000, 11000, 14000, 17000] } ]};
Again we’re defining a labels array which in this case is the names of the sales people. This time the datasets array has 2 objects, one for Total Sales and one for Gross Profit. For each dataset, which will represent a bar in the chart, we’ll assign different colour settings and of course difference data arrays. Each element in the data arrays represents a value for the corresponding salesperson in the labels array.
Now we’ve shown how to add a second dataset object to a chart, in this case by showing the Gross Profit for each salesperson, lets do the same by adding another dataset to the first chart’s data object (remember to put a comma immediately after the closing curly brace of the first dataset) :
{ label: "Gross Profit", fillColor: "rgba(218, 233, 39, 0.5)", strokeColor: "rgba(218, 233, 39, 1)", pointColor: "rgba(218, 233, 39,1)", pointStrokeColor: "#fff", pointHighlightFill: "#fff", pointHighlightStroke: "rgba(218, 233, 39,1)", data: [10000, 12500, 15000, 11000, 14000, 17000] }
Finally we’ll define data for the pie chart. Pie charts operate on different types of data. For example a pie chart cannot have different datasets - each element in a pie represents a single value. The data for pie charts is not defined in an object but as an array of objects with each representing a “slice” or an element of the pie. Each object in the pie array has to have a label, a value, a colour and a highlight colour. Here’s the sample data for our pie chart :
var dataSalesByRegion = [ { label: "West USA", color: "rgba(67, 214, 92, 0.5)", highlight: "rgba(67, 214, 92, 1)", value: 20000 }, { label: "UK", color: "rgba(218, 233, 39, 0.5)", highlight: "rgba(218, 233, 39, 1)", value: 12500 }, { label: "Asia", color: "rgba(230, 150, 200, 0.5)", highlight: "rgba(230, 150, 200, 1)", value: 25000 }, { label: "West Europe", color: "rgba(200, 220, 50, 0.5)", highlight: "rgba(200, 220, 50, 1)", value: 18000 } ];
With our data defined for each of our charts we can pass them into the relevant chart functions to create our graphs :
chartSalesByMonth = new Chart(ctxSalesByMonth).Line(dataSalesByMonth);
chartSalesByPerson = new Chart(ctxSalesByPerson).Bar(dataSalesByPerson);
chartSalesByRegion = new Chart(ctxSalesByRegion).Doughnut(dataSalesByRegion);
OK we’re ready to take a look at the static dashboard we’ve created. Open the index.html file in your browser and hey presto! there’s our dashboard. Have a quick play with the charts and notice all of the great functionality we get for “free” with ChartJS. For example the cool loading animation. We also get tooltips of each element’s data displayed as you mouse over a bar or a point in the line. For the pie chart, ChartJS picks the colour you declare in the highlight property along with a tooltip about the pie slice you’re hovering over.
One thing we’re missing here is a legend to let us know what’s going on. For example we have two lines on the first chart and two bars on the second yet there’s nothing to tell us what each one represents. Luckily ChartJS makes it super-simple to add a legend to our charts. Go back to your editor and open your index.html file. Add a new <div> tag underneath each <canvas> tag and give it an id that you can reference and give them all the class of chartLegend as below :
<div class="five wide column"> <canvas id="salesByMonth"></canvas> <div id="legendSBM" class="chartLegend"></div> </div>
<div class="five wide column"> <canvas id="salesByPerson"></canvas> <div id="legendSBP" class="chartLegend"></div> </div>
<div class="five wide column"> <canvas id="salesByRegion"></canvas> <div id="legendSBR" class="chartLegend"></div> </div>
In the firechart.js file we can define our legend simply by calling the generateLegend() function on each of the chart objects that we created. This returns the necessary html to display a legend for each chart and we assign them to the <div> elements we created above using jQuery selectors. The following code should be added immediately below the lines where we create our charts :
$("#legendSBM").html(lineSalesByMonth.generateLegend()); $("#legendSBP").html(barSalesByPerson.generateLegend()); $("#legendSBR").html(pieSalesByRegion.generateLegend());
Refresh your page in the browser and this time we have our legend. Pretty cool :)
Going back to our firechart.css, most of the classes are defined to style our legend nicely. For example the legend’s html is returned to display in a <ul> with each element displayed in a <li> tag. To avoid the bullet-points that normally come with <li>’s I set their display-type to none. Notice I had to define a class to do this for each type of chart I’m using - this shows how you can override ChartJS’ standard legend styles for each chart type individually. In the chartLegend li class I set it’s display to inline, this shows each element for the legend on the same line (with some padding added to space them out) rather than the default behaviour of showing each one on a different line. The chartLegend li span style is the most important related to the legend as it defines their layouts.
We’re done for the chart setup side of things. Next we’ll look at the trickier part - making our dashboard work with actual data from our Firebase and to make it dynamic and realtime. Before we crack on, here are the full listings for the index.html and firechart.js files so far. Notice I’ve added <h3> tags to each chart area in the index.html so that we have a header for each chart :
index.html
<!DOCTYPE html>
<html>
<head>
<title>Firechart Dashboard</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.1.8/semantic.min.css"> <link rel="stylesheet" href="firechart.css">
</head>
<body style="margin:25px">
<h1> Firechart Dashboard</h1>
<div class="ui grid">
<div class="five wide column">
<h3 class="align-center">Sales By Month</h3>
<canvas id="salesByMonth"></canvas> <div id="legendSBM" class="chartLegend"></div>
</div>
<div class="five wide column">
<h3 class="align-center">Sales By Salesperson</h3>
<canvas id="salesByPerson"></canvas> <div id="legendSBP" class="chartLegend"></div>
</div>
<div class="five wide column">
<h3 class="align-center">Sales By Region</h3>
<canvas id="salesByRegion"></canvas> <div id="legendSBR" class="chartLegend"></div>
</div>
</div>
<script src="http://code.jquery.com/jquery-1.12.2.min.js" integrity="sha256-lZFHibXzMHo3GGeehn1hudTAP3Sc0uKXBXAzHX1sjtk=" crossorigin="anonymous"></script> <script src="https://cdn.firebase.com/js/client/2.4.2/firebase.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/1.0.2/Chart.min.js"></script> <script src="firechart.js"></script>
</body>
</html>
firechart.js
(function() {
var ctxSalesByMonth = $("#salesByMonth").get(0).getContext("2d"); var ctxSalesByPerson = $("#salesByPerson").get(0).getContext("2d"); var ctxSalesByRegion = $("#salesByRegion").get(0).getContext("2d");
var dataSalesByMonth = { labels : ["Jan", "Feb", "Mar", "Apr", "May", "Jun"], datasets: [ { label: "Total Sales", fillColor: "rgba(67, 214, 92, 0.5)", strokeColor: "rgba(67, 214, 92, 1)", pointColor: "rgba(67, 214, 92,1)", pointStrokeColor: "#fff", pointHighlightFill: "#fff", pointHighlightStroke: "rgba(67, 214, 92,1)", data: [20000, 25000, 30000, 22000, 28000, 34000] }, { label: "Gross Profit", fillColor: "rgba(218, 233, 39, 0.5)", strokeColor: "rgba(218, 233, 39, 1)", pointColor: "rgba(218, 233, 39,1)", pointStrokeColor: "#fff", pointHighlightFill: "#fff", pointHighlightStroke: "rgba(218, 233, 39,1)", data: [10000, 12500, 15000, 11000, 14000, 17000] } ]};
var dataSalesByPerson = { labels : ["Bill", "Bob", "Jane", "Judy", "Max", "Holly"], datasets: [ { label: "Total Sales", fillColor: "rgba(67, 214, 92, 0.5)", strokeColor: "rgba(67, 214, 92, 1)", pointColor: "rgba(67, 214, 92,1)", pointStrokeColor: "#fff", pointHighlightFill: "#fff", pointHighlightStroke: "rgba(67, 214, 92,1)", data: [20000, 25000, 30000, 22000, 28000, 34000] }, { label: "Gross Profit", fillColor: "rgba(218, 233, 39, 0.5)", strokeColor: "rgba(218, 233, 39, 1)", pointColor: "rgba(218, 233, 39,1)", pointStrokeColor: "#fff", pointHighlightFill: "#fff", pointHighlightStroke: "rgba(218, 233, 39,1)", data: [10000, 12500, 15000, 11000, 14000, 17000] } ]};
var dataSalesByRegion = [ { label: "West USA", color: "rgba(67, 214, 92, 0.5)", highlight: "rgba(67, 214, 92, 1)", value: 20000 }, { label: "UK", color: "rgba(218, 233, 39, 0.5)", highlight: "rgba(218, 233, 39, 1)", value: 12500 }, { label: "Asia", �� color: "rgba(230, 150, 200, 0.5)", highlight: "rgba(230, 150, 200, 1)", value: 25000 }, { label: "West Europe", color: "rgba(200, 220, 50, 0.5)", highlight: "rgba(200, 220, 50, 1)", value: 18000 } ];
lineSalesByMonth = new Chart(ctxSalesByMonth).Line(dataSalesByMonth); barSalesByPerson = new Chart(ctxSalesByPerson).Bar(dataSalesByPerson); pieSalesByRegion = new Chart(ctxSalesByRegion).Pie(dataSalesByRegion);
$("#legendSBM").html(lineSalesByMonth.generateLegend()); $("#legendSBP").html(barSalesByPerson.generateLegend()); $("#legendSBR").html(pieSalesByRegion.generateLegend());
})();
I’ve already setup all of the data needed to generate the sales dashboard. To avoid having to type all of this data in manually yourself just head over to my GitHub page for FireChart (https://github.com/MancDev/firechart), download the zip containing the files, head over to your Firebase dashboard, click the Import Data button, select the firechartdata.json file and there you have all of the necessary data you need for this dashboard app. If you really can’t be bothered with any of that then you can just point to my demo Firebase reference as there’s no authentication on it - but I recommend you do import the data to your own Firebase to be able to view the data in your own Firebase dashboard.
The data structure is quite important here. Remember we’re not dealing with a relational database with tables and columns and niceties like sum and join. We’re dealing with a NoSQL object store where the purpose is speed and scale and we have to work with it accordingly. You can see that I have a node for sales and that leads us to another level of nodes. If we open up one of these child nodes we can see it contains a value for the name of the month and child nodes which each hold a piece of sales data. If we open one of these up we’ll see it contains some sales info for the salesperson, the region and the sales and profit. Now this structure is perfect for our first chart, Sales By Month, but will require more work to populate our 2nd and 3rd charts. It’s quite common in NoSQL databases to set up different data structures to suit the requests and queries you want and to duplicate the data in several places to optimise speed. However in this case I’m just storing one set of data and we’ll do the work we need to do on the client to get the data calculated to populate each chart. In doing so we’ll be highlighting some useful techniques to query data in Firebase.
Let’s get started by adding the following lines at the top of your firechart.js file, just below the opening (function() { :
var firebaseRef; var salesRef;
Add all subsequent code we include from hereon in at the very bottom of the firechart.js file (a full listing of the completed code will be included at the end).
We’ll wrap our initialisation code inside a (document).ready() function. We’ll create a reference to the root of our database by newing up a Firebase object. Then we’ll add a reference to the sales node which is what we’ll be working with by using the child() method on our root reference.
$(document).ready(function() { firebaseRef = new Firebase("https://scorching-fire-224.firebaseio.com"); salesRef = firebaseRef.child(“sales”); });
Substitute the Firebase Url given with your own.
To retrieve data from a Firebase reference we call a method and pass it a callback function. The most common method to get data from Firebase is the on() method. This will get the data the first time that we call it and sets up a listener to constantly listen for the type of changes we want. In this code we’ll call on() for our salesRef reference that we assigned and tell Firebase we want to listen for any changes to the data on that reference.
salesRef.on(“value”, salesListener);
Add the above line as the last line in the (document).ready() function.
The first parameter is the event that we want to listen for. The value event that we’re passing is telling Firebase that we want to know when any value is changed in our sales data reference. This includes any change, whether data is added, updated or deleted, we want the event to fire for all changes. There are other event types we could listen for - for example if you just wanted to listen for when objects are added to that reference you’d pass in “child_added” as the first parameter instead.
The second parameter we pass is our callback function which contains the code we want to execute every time there is a change in the data. This is where Firebase’s realtime capabilities start to shine. We can make our dashboard realtime and dynamic simply by listening for changes on our sales data and calling a function which will redraw the charts based on the updated data. Remember that the on() method immediately fires the callback function so the code to initially draw our charts, or rather populate the data into our charts, is the same to initialise as it is to handle changes.
As an aside, if you find yourself needing to query a Firebase reference to get some data but don’t want to continue to listen for changes then you can call the once() method instead of on().
We now need to write the salesListener callback function to implement the logic which will utilise the data to populate our charts. Let’s start by implementing the Sales By Month chart.
It can be easy to get confused about how the data is structured so the best way to have a picture of the data you’re dealing with is to click on the relevant node on the Firebase dashboard, in this case the sales node. This tells you exactly what data we’ll be getting from that node reference. In this case it’s a series of child nodes with each one representing a particular month. Inside each of those month nodes are data objects containing the actual sales data and a property containing the month name. Our purpose is to sum up the sales and profit totals from all of the sales records for each month and grab the month name for the chart labels.
In our listener function we start off by initialising our values. We’ll be using 2 variables to sum up our sales and profit values for each month and once each month is dealt with we’ll push those values into our data arrays for the chart. Each time our listener function is called we’ll reset the chart’s data arrays first :
var salesByMonthListener = function salesByMonthListener(snapshot) {
var sales var profit;
dataSalesByMonth.labels = []; dataSalesByMonth.datasets[0].data = []; dataSalesByMonth.datasets[1].data = [];
Notice that in our callback function we have a parameter called snapshot. This is an object passed in by Firebase and it contains the data that we’ve asked for along with lots of other tools to deal with that data. The actual data is accessed by calling snapshot.val(). In this case simply calling val() straight on the snapshot won’t give us too much as the sales node itself doesn’t contain any useful data as it’s contained in child objects. Fortunately the snapshot object contains a forEach() method which enables us to iterate over it's child nodes. A quick look at the Firebase dashboard will show us that the immediate child nodes are objects each representing a month. So we need to use forEach() to iterate over our month objects.
forEach() expects another callback function which is triggered for each iteration over a child node. Our forEach() callback function also receives a snapshot parameter which contains just the data for that child node.
snapshot.forEach(function(salesMonthSnapshot) { sales = 0; profit = 0;
Each time the forEach() callback function is called it represents a new month, so we’ll reset our variables for totalling up the sales and profit to zero. We can now use the salesMonthSnapshot to reference the data specific to the current child node which represents a month. Again we get at the actual data by using the val() method. As the name of the month is stored on the node we’re dealing with, we can use salesMonthSnapshot.val() . Once we’ve got the name of the month we can push it into the labels array on our chart.
thisMonthName = salesMonthSnapshot.val().month; dataSalesByMonth.labels.push(thisMonthName);
Now things get interesting. The actual data necessary for us to sum up our monthly sales and profit values are nested in another level of child objects so we can’t simply use salesMonthSnapshot.val() to access that data as we did with the month name. So how can we access them? We use the forEach() method again for this next level of nodes and iterate around them. Each level of nested nodes has it’s own forEach() method to which we can assign a callback which receives the snapshot for data specific to that level. Again the Firebase dashboard is useful in giving us a visual picture of what we’re dealing with by clicking into each level of node to see what each snapshot will give us.
salesMonthSnapshot.forEach(function(salesSnapshot) { });
We’re now dealing with a third level snapshot which we’re calling salesSnapshot. The callback function to deal with this is relatively straightforward - it needs to call the val() method of the salesSnapshot and increase the value of the total sales and profit by the sales and profit properties on each sales object.
There is a fly in the ointment here though - the month property, which we grabbed using val() in the higher level callback, is still considered a child node and will be iterated over by forEach(). That node does not contain properties for sales and profit and when we attempt to increase the totals using these non-existent properties it will result in an incorrect value.
We can however deal with this quite simply using the hasChildren() method. Checking to see if the salesSnapshot of the current node has children effectively tells us whether the node we’re dealing with is an object with properties or if it’s simply a value. This means we can ignore anything that doesn’t have any child properties. Once we’ve determined that the current node is a valid object we can simply increase the sales and profit values by calling val() on the salesSnapshot and the name of the property we want.
salesMonthSnapshot.forEach(function(salesSnapshot) { if (salesSnapshot.hasChildren()) { sales += salesSnapshot.val().sales; profit += salesSnapshot.val().profit; } });
We now have the total sales and profit values for the month we’re dealing with and back in the second level callback function we can pass these totals into the data arrays for the chart. Remember that we have 2 datasets on the chart data, one for sales and one for profit and we push each value in to the data array on the appropriate dataset.
dataSalesByMonth.datasets[0].data.push(sales); dataSalesByMonth.datasets[1].data.push(profit);
})
The second level callback function, dealing with each specific month, has done it’s job so we fall back out to the original listener callback function. We now have all the data we need in the chart's data object so we now pass it into our chart. To make sure we have a fresh chart object each time a change is triggered we reset the chart object variable to a blank object and then re-create it. We then re-generate our chart’s legend :
chartSalesByMonth = {}; chartSalesByMonth = new Chart(ctxSalesByMonth).Line(dataSalesByMonth); $("#legendSBM").html(chartSalesByMonth.generateLegend());
Refresh your page in the browser and you’ll still have your 3 charts except the Sales By Month chart will now be generated from live data in your Firebase.
Open your page and the Firebase dashboard in two separate browser windows and put them side-by-side. In the sales data make changes to the sales and profit values and watch your chart update in realtime. Pretty cool or what?!
OK so we’ll close off here and in part 2 I’ll complete the app by going through how to query the data to populate the other 2 charts which are little bit more tricky. See you there!
PS : Here’s a listing of the finished JS file :
firechart.js
(function() {
var firebaseRef;
var ctxSalesByMonth = $("#salesByMonth").get(0).getContext("2d"); var ctxSalesByPerson = $("#salesByPerson").get(0).getContext("2d"); var ctxSalesByRegion = $("#salesByRegion").get(0).getContext("2d");
var dataSalesByMonth = { labels : [], datasets: [ { label: "Total Sales", fillColor: "rgba(67, 214, 92, 0.5)", strokeColor: "rgba(67, 214, 92, 1)", pointColor: "rgba(67, 214, 92,1)", pointStrokeColor: "#fff", pointHighlightFill: "#fff", pointHighlightStroke: "rgba(67, 214, 92,1)", data: [] }, { label: "Gross Profit", fillColor: "rgba(218, 233, 39, 0.5)", strokeColor: "rgba(218, 233, 39, 1)", pointColor: "rgba(218, 233, 39,1)", pointStrokeColor: "#fff", pointHighlightFill: "#fff", pointHighlightStroke: "rgba(218, 233, 39,1)", data: [] } ]};
var dataSalesByPerson = { labels : ["Bill", "Bob", "Jane", "Judy", "Max", "Holly"], datasets: [ { label: "Total Sales", fillColor: "rgba(67, 214, 92, 0.5)", strokeColor: "rgba(67, 214, 92, 1)", pointColor: "rgba(67, 214, 92,1)", pointStrokeColor: "#fff", pointHighlightFill: "#fff", pointHighlightStroke: "rgba(67, 214, 92,1)", data: [20000, 25000, 30000, 22000, 28000, 34000] }, { label: "Gross Profit", fillColor: "rgba(218, 233, 39, 0.5)", strokeColor: "rgba(218, 233, 39, 1)", pointColor: "rgba(218, 233, 39,1)", pointStrokeColor: "#fff", pointHighlightFill: "#fff", pointHighlightStroke: "rgba(218, 233, 39,1)", data: [10000, 12500, 15000, 11000, 14000, 17000] } ]};
var dataSalesByRegion = [ { label: "West USA", color: "rgba(67, 214, 92, 0.5)", highlight: "rgba(67, 214, 92, 1)", value: 20000 }, { label: "UK", color: "rgba(218, 233, 39, 0.5)", highlight: "rgba(218, 233, 39, 1)", value: 12500 }, { label: "Asia", color: "rgba(230, 150, 200, 0.5)", highlight: "rgba(230, 150, 200, 1)", value: 25000 }, { label: "West Europe", color: "rgba(200, 220, 50, 0.5)", highlight: "rgba(200, 220, 50, 1)", value: 18000 } ];
var lineSalesByMonth; barSalesByPerson = new Chart(ctxSalesByPerson).Bar(dataSalesByPerson); pieSalesByRegion = new Chart(ctxSalesByRegion).Doughnut(dataSalesByRegion);
$("#legendSBP").html(barSalesByPerson.generateLegend()); $("#legendSBR").html(pieSalesByRegion.generateLegend());
$(document).ready(function() {
firebaseRef = new Firebase("https://scorching-fire-224.firebaseio.com");
var salesRef = firebaseRef.child("sales");
salesRef.on('value', salesByMonthListener);
});
var salesByMonthListener = function salesByMonthListener(snapshot) {
var sales; var profit;
dataSalesByMonth.labels = []; dataSalesByMonth.datasets[0].data = []; dataSalesByMonth.datasets[1].data = [];
snapshot.forEach(function(salesMonthSnapshot) { sales = 0; profit = 0;
thisMonthName = salesMonthSnapshot.val().month; dataSalesByMonth.labels.push(thisMonthName);
salesMonthSnapshot.forEach(function(salesSnapshot) { if (salesSnapshot.hasChildren()) { sales += salesSnapshot.val().sales; profit += salesSnapshot.val().profit; }
});
dataSalesByMonth.datasets[0].data.push(sales); dataSalesByMonth.datasets[1].data.push(profit);
})
lineSalesByMonth = {}; lineSalesByMonth = new Chart(ctxSalesByMonth).Line(dataSalesByMonth); $("#legendSBM").html(lineSalesByMonth.generateLegend());
};
})();
2 notes
·
View notes
Text
Cloud-enabling a legacy desktop app using Firebase
For the past 3 1/2 years my primary focus has been on web development however the thing that actually keeps the lights on in my business is an 8 year old Windows desktop app. I’ve got a lot of loyal small business customers paying me year-in-year out for this app and for the updates I release for it. Most of these customers are cloud-averse - a particular reason they chose a desktop app in the first place and not one of my many cloud-based competitors. This desire to have their own information on-premise has of course been good for my business yet despite their opinions on having their information in the cloud they are growing more and more anxious to have an app that will give them access to their information for remote employees or for when they’re on-the-go on their tablets or phones. In other words they want it every which way! And why not?
I’ve toyed with a solution to this for the past couple of years and even though I had a picture of how I wanted it to work, the answer to how I could do it in a reasonable timeframe and produce a performant, reliable and trustworthy solution was elusive.
Here’s the problem domain :
Customers want to have access to their information remotely via a mobile and/or web app.
Customers don’t want all of their key information in the cloud. They want to keep it on-premise.
They don’t want to turn their internal server into a web server e.g. for cost, security & maintenance reasons.
If they do have to have small amounts of their data in the cloud it has to be on an enterprise-grade cloud platform.
A couple of weeks ago the answer popped right in to my head from nowhere! Fundamental to that answer was Firebase.
While I’ve spent a lot of time a year or so ago looking into and learning Firebase and was very impressed with it, I had a couple of concerns for developing full production apps with it. My main concern was that it required you to effectively code your app against a hosting platform. If Google dropped Firebase, as Facebook subsequently dropped Parse, or their pricing got too abrasive you’d be looking at a fairly hefty rewrite just to adjust to your new hosting requirements. Also there is no server-side code for Firebase, all of your code needs to run on the client or you’ll need to put your sensitive code on another host behind a REST API . But recently I decided to revisit Firebase. I have to say I cannot help but like Firebase, their approach is spot on and a review of their recent pricing changes proved they were listening to and addressing the needs of indie devs and not just to their enterprise clients.
It was a couple of weeks after getting that fuzzy feeling about Firebase again that the answer to my desktop app cloud requirement suddenly zipped into my head. And it answered all four points of the problem domain :
Customers want to have access to their information remotely via a mobile and/or web app. Develop a small web/mobile client app that allows the user to view certain snapshots of information from their on-premise database.
Customers don’t want all of their key information in the cloud. They want to keep it on-premise. This solution enables them to keep their core database on-premise with only tiny requested snapshots of data in the cloud. Their one-source-of-truth is still their on-premise database. Data held in the cloud operates like a cache and can be wiped regularly, either automatically or manually without effect.
They don’t want to turn their internal server into a web server e.g. for cost, security & maintenance reasons. Provide a small middleware app which runs natively on their server connected to their on-premise database. Firebase provides the realtime technology and the cloud datastore to handle the request/response cycle without a direct client connection to the on-premise database and without non-performant and fragile polling of requests.
If they do have to have small amounts of their data in the cloud it has to be on an enterprise-grade cloud platform. Firebase, being part of Google, hosts it’s datastore on Google Cloud Platform, one of the “Big 3” cloud platforms.
The key to all of this is to have no connection from the client app or the Cloud to the user’s on-premise server. Although it will appear, when using the client app, to work as though there were a connection, there is actually only a one way connection from the customer’s server to Firebase and that involves listening for requests added to Firebase by the client app and pushing snippets of requested data back to the data cache on Firebase. Neither the client app nor the middleware app running on the customer’s server have any knowledge of each other, they are only pushing data and listening for data on Firebase. Firebase is perfect for this request/response methodology as it deals with everything in realtime making this extremely performant by removing the need for polling or throttling.
Key to all of this is, due to Firebase’s simple API, it could be developed very quickly - proven when I completed the initial Proof-of-Concept in under 4 days including having to do a lot of learning-as-I-went and with most of that time spent fleshing out features on the client app.
One hurdle I had to overcome was developing the Windows middleware service app. Firebase don’t provide a client library for .NET or C#. Fortunately the community has filled this gap and there are a couple of options. The one I went with was FirebaseSharp by Robert Horvick . He has a V1 and V2 library. The V2 library is still in Alpha but I decided to use it as the API is very similar to Firebase’s JS API and it meant I didn’t have to wrestle with C#’s async … await to handle the asynchronous processing of Firebase’s realtime updates. While still in Alpha I haven’t had any issues in working with FirebaseSharp V2 and it’s been a pretty much seamless experience. You can add FirebaseSharp to your C# project in Visual Studio through NuGet - remembering to tick the Include prerelease box as V2 of FirebaseSharp is officially still in Alpha.
Here’s how it works : The middleware app (which I decided to call the “Bot”) runs as a service on the customer’s server machine. It’s very lightweight and has a primary task of listening for requests which it achieves simply by setting a reference to the Firebase datastore and then setting a listener event for any child objects being added to the request node. Using FirebaseSharp’s API we can use the On() method, similar to how we would in JS, and select to listen to the “child_added” event on the request node.
C#
FirebaseApp fireBase = new FirebaseApp(new Uri(“https://your-firebase-database.firebaseio.com”, “YouRFirEbaSESecRetKEy”);
var requestRef = fireBase.child(“request”);
requestRef.On(“child_added”, (snapshot, child, context) => {});
On the client app we need to add user requests to the request node on Firebase for the Bot to respond to. We do this by defining a request object which the Bot can read and deal with. This can be any format you like but I wanted the client app to be able to know if the request had been received by the Bot and to display that status to the user. Again Firebase makes this super-simple by grabbing a reference to the request object that’s pushed to Firebase and then immediately listening for updates to that reference. This is done by listening to the “value” event on the specific request object that the client saved.
JS
var fireBase = new Firebase('https://your-firebase-database.firebaseio.com');
//get a reference to the request node
var requestRef = fireBase.child('request');
//define the request object that we’ll save to Firebase
var requestObject = {requesttype:'getCustomerSalesInfo',
id:'0',
reference:'',
received:false,
response:false,
requesttimestamp:(new Date()).toString()
};
//save the request to Firebase and grab a reference to it
var newRequest = requestRef.push(requestObject);
//set a listener reference for any changes made to the new request
newRequestRef = requestRef.child(newRequest.key());
//put an event on that listener reference to check if it gets updated
newRequestRef.on('value', function(snapshot) { } );
In the on() event listener we can display a message which lets the user know that the Bot running on their on-premise server has received the request and is dealing with it.
On the Bot we receive the request through the listener event which we setup to listen for new requests. To tell clients that are listening for a response that the request has been received, we update the record object by changing it’s received property to true and also saving a message which the client can use to show to the user :
C#
requestRef.On(“child_added”, (snapshot, child, context) => {
var receivedRequestReference = requestRef.Child(snapshot.Key);
//update the status of the request object on Firebase to received and also save a message for the client to show
receivedRequestReference.Update(“{\”received\”:true, \”receivedmessage\”:\”Request received OK!\”}”);
});
Once the client is aware that the request has been received it’s next task is to listen for the data that’s being populated by the Bot on the user’s server :
JS
newRequestRef.on('value', function(snapshot) {
//show the message from the Bot that the request has been received document.getElementById('requestReceivedMessage’). value(snapshot.val().receivedmessage);
//get a reference to the data node that we want to listen for
var listenerRef = dataRef.child('customersales');
//set a listener so we know when the data that we requested is pushed to Firebase so we can show it on our page
listenerRef.on('value', function(snapshot) { });
})
The Bot, based on the properties of the request object, goes away and queries the user’s on-premise database for the necessary information that’s been requested. It then pushes that snippet of data to the Firebase node which the client is now listening for. The listener on the relevant node on the client app picks up the data that the Bot has pushed to Firebase and can then display it as is necessary on the page whether in a table or a chart etc.
Obviously there’s a lot more going on to make all of this work but those extras are specific to my application. What I’ve explained are the core elements of what makes this request/response cycle possible without any connection to the user's on-premise server machine but with the ability to expose the data held there to web and client mobile apps in a secure and performant way. Critically this has the potential to free my customers from being stuck to their office PC to get at their core business information. That this was ultimately achieved with barely a dozen lines of combined C# and JS code is testament to Firebase’s power and flexibility to quickly build realtime data apps and has taken away so many of the headaches that would otherwise hamper a project like this.
What about performance? The realtime performance of this solution thanks to Firebase is staggeringly fast. The only performance overhead is the time Firebase takes to push objects/records to it’s datastore. If saving a couple of records it’s hardly noticeable but as, in this case, I may push 30+ records in a single operation into Firebase in response to a request and this is where it starts to look sluggish. However considering what it’s doing I think it’s an acceptable overhead but just be conscious of the amount of data you’re pushing in to Firebase at any one time and you should be fine. To be honest my customers, even in early preview, are blown away by this solution despite this.
In the interests of fairness I should point out that I could have achieved similar results with other realtime frameworks and platforms. I’m particularly keen on Meteor although their hosting plans didn’t suit this app. I also considered Syncano which has the advantage over Firebase of being able to run server-side code and has the same simple platform hosting capabilities however doesn’t have that credibility factor of being hosted on Google’s Cloud Platform which is an easier sell to my customers for this particular scenario. I also love working with Firebase’s simple API - it’s just sooooo sweeeet. For the C# side of things I owe a big Thank You to Robert Horvick for his FirebaseSharp V2 package - go check it out!
0 notes
Text
Javascript has Classes. Hurrah! Or Not?
ES6 (or ES2015) is the next version of Javascript and includes a number of changes to the language including the introduction of the Class keyword from classical Object Oriented Programming (OOP). Hurrah! Coming from an Object Pascal (Delphi) and a C# background, classes are the foundation of everything I’d done for the previous 15 years. Having worked solidly in Javascript for almost 3 years now I’d gotten used to life without them and had learned to live within the “confines” of a classless language and even enjoyed some parts of using prototypes - or shall I say the bits I understood about prototypes. However I must confess that I’d missed classes when developing in JS and the idea of using them within Javascript was exciting for me.
For a new project I thought I’d get going with using Babel and maybe Typescript and make liberal use of classes and the classical OOP inheritance and polymorphic “goodness” they bring. As I expected, when classes are transpiled to ES5, the resultant code is utilising prototypes to mimic classical OOP. Fine. However I began to discover that browsers will not be utilising classes natively in their ES2015 implementation of Javascript. Effectively classes will just be syntactic sugar over prototypes just like the transpiled ES2015 code. As a realisation came over me I understood what this meant! Classes are being introduced into JS as a sweetener to satisfy programmers of classical OOP who don’t want to change. The bottom line was this - natively, Javascript will NOT use classes and for very good reasons!
I read a blog post by Eric Elliott recently which sums up the situation nicely :
Prototypes are the idiomatic inheritance paradigm in JS, and `class` is the marauding invasive species.
Javascript was never designed to use classical OOP and no-one, not Google, not Microsoft and not Apple are going to make it that way. So if the language doesn’t actually use classical OOP and classes are just there to make it look that way in order to take the pain away for people like me then is it really worth using? Wouldn’t it be better to actually delve into the language proper and use the native capabilities of the language? After all this language is swallowing the world - it’s not just some obscure academic thing. Suddenly I felt I was on the wrong side of history - again. I remember back in the mid-90s when classical OOP was hitting critical mass I was quickly made to feel like an imbecile for not knowing it. I realised I was on the wrong side of this and I had to change the way I thought about development. It took a while to fully appreciate it’s benefits but when it “clicked” a whole new world opened up. I now feel the same in not fully grasping the benefits of the native prototypal OOP in Javascript. It was time to force myself into that paradigm shift again and change the way I thought about development.
One of this biggest shifts for me when I first started developing in Javascript in earnest were functions. I was used to using functions/methods as delegates for events going all the way back to my Delphi days. Lambdas in C# certainly helped prepare me but it took a real shift in thinking to realise the potential of functional programming. It took me years in fact to truly make that mental shift and when today I read expert’s blogs and articles (such as Kyle Simpson and Eric Elliott) I still am a little insecure about whether I truly grasp it now. However I have forced myself to fully grasp prototypal OOP and endured another necessary cognitive paradigm shift. It has certainly altered my perspective on using ‘class’.
In JS with prototypal OOP there is really no such thing, in essence, as inheritance although it can look and be worked to appear that way. For example declare a function on an object’s prototype, create a 2nd object that uses (or is linked to) the first and they will share the same prototype and therefore the same functions using prototype chaining. Therefore attaching functions to an object’s prototype can be made to “appear” to be like adding methods on a class and that the first object is the class and the second object is instantiated from that class. WRONG!! Yet this has opened up the proverbial pandora’s box for class and classical OOP to "invade” a language like Javascript which has no concept of them. So what? you may say, we use syntactic sugar everywhere in modern languages. Indeed but there’s a difference between a little sprinkling of sugar to make coding faster and more readable and what is happening here which is trying to put a round peg in a square hole. If you use Class it will make you think in the classical OOP way and that’s dangerous because you’ll expect it to work that way throughout. Transpilers and ultimately the browsers may give you a little help with that but remember it’s a round peg in a square hole and at some point you will hit the skids and then .. well good luck. How? I could go into a great deal of detail about this (check out Eric Elliott’s blog) and I probably will in a future post but I’ll just mention one word (or rather one keyword) - ‘this’. Do some research on how ‘this’ works in prototypal OOP in JS and try and spin that one into classical OOP. Just looking at how fundamentally different ‘this’ works in JS compared with ‘this’ or ‘self’ in classical OOP languages should be enough to convince you that using ‘class’ in JS will just drive you into a quagmire.
So how should we use the native prototypal OOP in Javascript to properly manage issues such as encapsulation which classes take care of in other languages? The answer is Composition.
Before I go on, I’d highly recommend watching this short yet very informative and amusing video on why you should use Composition in JS rather than resort to using classical inheritance.
Composition is where an object comprises from multiple objects rather than from a single parent. It’s important to note that it is comprised from objects not from classes. Prototypal inheritance works with instances of objects so when you inherit or compose a new object from others you’re effectively linking objects together and being able to access the properties and functions of the linked object and of course the functions on the linked object’s prototype. This means that you develop a flat object structure which you can think of as “has a … “ or “does …” rather than “is a …”.
I like the analogy that “You can choose your friends but you can’t choose your family”. With inheritance your objects have a parent. They can’t change that parent and they inherit all of the characteristics of that parent, whether they need or want them or not. Inheritance is more about the identity of the object eg it’s type. With composition on the other your objects can choose which other objects they are composed of and which behaviours they adopt. If an object’s behaviour is not wanted, simply don’t use it in it’s composition. Therefore an object’s composition and it’s behaviours are very flexible and not predetermined by it’s parent class as is the case with classical inheritance.
Composition also encourages a very clean way of coding because it naturally forces you into keeping your objects very small and tight with each one performing a small number of behaviours. It leads you into creating a layer of objects which are pure abstractions that are only used to compose objects that you’ll actually utilise in your app. This makes you think of developing small blocks of functionality and then compose a new object comprised of those blocks and add more functionality to do something useful in your app. Overriding methods from a linked object’s prototype is also very straightforward and extremely handy. You are not constrained at all by a hierarchal structure and can pick-and-choose what you need from other objects and override as is necessary. It’s actually very freeing! Rather than constructors you use factory functions which spit out the objects you need as you need them. Closures provide the encapsulation story.
It could be argued that you could use Class just as a container or an encapsulation device and I certainly am regularly tempted by this but there’s potentially two problems with that - 1) you’ll be tempted to extend your class using the syntactic sugar on offer and 2) you’ll lose the benefits of composition.
I’ll go into code examples in another post but I just wanted to get the theory out of the way first. It does require a shift in thinking but fundamentally classical OOP and Class is simply not part of JS. Ultimately it’s best to use the language how it was meant to be used. This is Javascript after all - and it’s an ugly-beautiful thing!
For more information I’d highly recommend Kyle Simpson’s series of book ‘You Don’t Know JS’ especially the one titled ‘this & Object Prototypes’ and if you have a Pluralsight subscription check out his ‘Advanced Javascript’ course. Also essential reading is Eric Elliot’s book ‘Programming Javascript Applications’
0 notes
Text
Gulp, Grunt, Bower, Yeoman?? What's it all about?
Let's face it, the Web Development world is over-loaded with buzzwords - framework names, technologies, libraries, automation tools and testing frameworks than one man can feasibly learn in a lifetime - and there's more being released all of the time. Since the introduction of NodeJS which enabled Javascript to be run outside of the browser we've also seen an explosion of Javascript build tools, task runners and package managers to help developers manage the frameworks and libraries that we work with. On the face of it, it can appear to be just another new set of names and methodologies to learn and remember and who needs more of that right?!
For a while I just ignored them hoping that I'd never need to bother learning them - didn't I already have enough to learn?! However I came across a new SPA framework which I thought would be interesting to learn about. In it's Getting Started guide there was no mention of download links to libraries, no mention of CDNs - just a list of instructions about using npm and Bower and Gulp. It dawned on me there and then, as I read through this list of tools requirements, that these build tools had now become like oxygen to the web development world and I was effectively persona non grata until I knew how to use them. Just relying on links, CDNs and the good ole F5 key just isn't going to cut it anymore in this new frontier of web development.
So I set about getting my head around all of this with the aim of putting together a quick reference for myself. I decided to put together an overview of exactly what each one does and to also find some conceptual common ground with the stuff I was already used to. Package managers were something I was used to going all the way back to my Delphi desktop development days in the 90s and 2000s with component packages and in more recent times using the excellent Nuget in Visual Studio. Automated build tools and task runners were baked into every IDE I'd ever used and had been greatly missed when I switched over to Web Development although over time I'd learned to accept life without them. So once I got my head around what these tools were trying achieve, the lightbulb went on and the word "awesome" sprung to mind. I hope this overview will help you in understanding these build tools if you didn't already and in doing so help make the world a better place ;)
Node(JS)
I think everyone knows that Node is, basically, a Javascript server which executes Javascript on a machine outside the browser. The enabling of Javascipt code to execute directly on the machine outside of the browser runtime has caused a plethora of tools to be developed to help automate the Javascript/web development process. Node is very cool and if you don't want to be left developing in 2012 for the rest of your career I strongly suggest you bite the bullet and install it. More about Node later.
The Command Line
As I wrote the sub-header above I imagined the film Young Frankenstein whenever the name "Frau Blucher" is mentioned causing thunder & lightning to strike and the horses to cry in terror (https://www.youtube.com/watch?v=zdIID_TGwhM). Substitute "Frau Blucher" with "The Command Line". If having to use a command line to set-up your development environment fills you with the same horror as the horses felt about Frau Blucher then I know where you're coming from. For the first 5 or 6 years of my career in the early 90s I used command line tools almost exclusively. Yet after almost 20 years of not needing them it still seriously freaked me out at first that I now had to use them again. All of these Node-based tools require the use of commands entered in a terminal window - referred to as CLIs. The reason is straightforward - Node can execute Javascript code but without it's graphical runtime, the browser, there's very little else it can provide in the way of a GUI than to accept input and output from/to the terminal prompt. Don't let having to use the command line stop you, just dive in and it'll soon be like second nature. (By the way if you haven't seen Young Frankenstein you really should make sure you do!)
Homebrew
You'll frequently hear mention of Homebrew. Homebrew has very little to do with web development and is actually a package manager for MacOS X (the equivalent on Windows is Chocolatey). If you're not familiar with what a package manager is it's basically a tool to help you manage the software that you download to your machine, helping you locate the software packages you need and keeping all of your installed packages up-to-date. Homebrew is a command line tool to install any OS X package you want and you can use it to install Node onto your machine if you want or you can just head over to nodejs.org and click Install on their homepage to download and install Node. If you want to use Homebrew or Chocolatey head over to brew.sh or chocolatey.org and just copy the command provided on their respective home pages for the OS you're using and paste it into your terminal command line and you're away. Either way don't worry too much about Homebrew - at least now you know what it is anyway.
NPM
Apparently an acronym for Node Package Manager, NPM is a tool which is generally installed with Node. While Homebrew is a package manager for handling Mac application packages and downloads, NPM performs the same purpose for handling Javascript-based packages to be run on Node. NPM is pretty straightforward and keeps a track of the Node packages you've downloaded, helps you update them and makes it really easy to install new ones. If you've used Visual Studio before, NPM is effectively the equivalent of Nuget - just without the nice GUI. You will use NPM to download and install the remaining elements explained here and it's as simple as typing :
npm install -g <packagename>
Make a note that the -g attribute tells npm to install the requested package globally on the machine and makes it available from everywhere. If you exclude the -g then the package will only be installed inside the folder that you're in when you run the command. If you have problems executing any command in OS X you should to try again and precede the command with sudo and pop-in your Mac user password when asked. This is to satisfy OS X that you have the appropriate permissions to install the package to your machine.
Before you install any packages using npm for your project you should run the following command when inside the root folder that you'll be using for your project's development :
npm init
This will initialise your project directory with a package config file called package.json and this enables npm to keep a log of the packages you have installed for your project, the version numbers etc. This is useful if you want to set your project up on another machine as you can copy the package.json file in and run npm install and npm will pull in every package described as dependencies in the package.json file. Cool eh?
Bower
Bower is, like NPM, a package manager. Whereas NPM downloads and manages packages to be run on Node for your development process, Bower is used to manage and download client-side packages which will be used in your actual application. For example lets say you want to use AngularJS or JQuery in your application. Angular and JQuery are not Node packages to be run on a server (neither during development or run-time) but are to be referenced in your client-app. In the "old days" you'd download from a link on the respective libraries' web-site or put a link to a CDN but Bower is now the standard way to consolidate these client-side libraries into your app project. Now it's not much use if you only use one client-side library in your app but the chances are that if you are developing even a reasonable-sized app you'll be using quite a number of client libraries and frameworks and Bower helps you organise, update and manage all of these during development and in your build process without having to go out and manually check for updates at each of the framework's web-sites.. You'd generally install Bower using NPM by using
npm install -g bower
Like NPM if you run bower init before installing anything it creates a bower.json file which keeps a record of all of the libraries and dependencies you've installed for the project.
It's important to remember that you will need to have git installed on your machine before you can start downloading packages with Bower as it downloads all of it's packages through git. Check out http://www.git-scm.com/ and click the Download option on the homepage. After installing git you'll likely need to log-out and log-in on your machine before it will work with Bower.
JSPM
JSPM is an alternative to Bower and performs pretty much the same functions but is aimed at future-oriented ES6 (next gen Javascript) frameworks. While it's still quite esoteric compared to Bower I have started to see it prop up more and more in blog-posts and as the default package manager for more recent client-side frameworks (such as Aurelia). One to keep an eye on for sure.
Gulp
Gulp is a task runner and automated build tool and as with all of the Node-based development tools you install it using NPM ie
npm install -g gulp
Gulp is kinda like a tool to help you organise your web application files into a manageable project both during development and for production. If you come from a .NET and a VS background, your ASP.NET, WinForms or WPF files and projects are handled and managed for you using tools built into VS to automate the build process from countless source files. Gulp is the tool that is designed to perform this type of build management for you in your web app. It also acts as a task runner to automate your build process when changes are made to your HTML, CSS or Javascript and is particularly useful if you use a CSS pre-processor such as SASS and/or a Javascript transpiler like 6to5 or Typescript by automating the compilation steps for you. Gulp makes extensive uses of plug-ins to carry out the automated tasks you want. For example if you want to have Gulp automatically compile your SASS for you you'll need to download the gulp-sass plug-in and for transpiling your ES6 code using 6to5 you'll need the gulp-6to5 plug-in. The great thing about this is that anyone can write plugins for Gulp which has resulted in a huge library of plug-ins available from gulpjs.com for just about anything you'll ever likely need.
To get Gulp to do all of this fancy stuff you do have to write some Javascript in a gulpfile.js file and to use the Gulp APIs to import plug-ins such as the SASS pre-compiler or the minimiser (also called the uglifier) and run the tasks you want. There is a learning curve involved and depending on your project it can become quite complex although to get the basics going it's reasonably straightforward. You can set watch tasks on particular files in your project so that when they change, certain tasks are performed. As an example let's say you're using SASS and you put a watch on all of the .scss files in your project styles folder so when a change to any of your .scss is made the SASS pre-processor is automatically called and outputs the compiled css to where you want it and can refresh your browser to see your changes instantly. Another good example is if you're using Typescript. Rather than writing your code, running the TypeScript compiler at the command line and refreshing your browser each time, you simply use the available TypeScript plug-in for Gulp, put a watch on all of the .ts files in your project and assign a watch task so that when any change occurs in one of your .ts code files it automatically transpires your TypeScript code and outputs to Javascript .js files and refreshes in your browser to run the change. Imagine if you work with Typescript (or CoffeeScript or AtScript or ES6 etc) and/or SASS or LESS how much time this could save you each day.
I'm really just trying to give you an overview here and it's beyond the scope of this post to explain how this is all done but I'd definitely recommend you check-out the free video course on YouTube from LevelUpTuts at https://www.youtube.com/playlist?list=PLhIIfyPeWUjoySSdufaqfaSLeQDmCCY3Q . If you're posh like me ;) and have a Pluralsight subscription (or setup a free trial) check out John Papa's in-depth training course on Gulp at http://www.pluralsight.com/courses/javascript-build-automation-gulpjs
I avoided learning anything about Gulp but now I've finally taken the plunge to learn it, I have to say it's freakin' awesome and wish I'd jumped in sooner. As we move to majority and full-stack, large scale Javascript app development, tools like Gulp are going to be fundamental to our ability to get our apps completed quickly and out the door to our users. The F5 key and manually typing in commands each time may have been acceptable before but now we're into serious JS development, Gulp and tools like it are going to be our very best friends.
Grunt
Grunt is an equivalent/competitor tool to Gulp but apparently older and more complicated to set-up as it uses JSON config files rather than Javascript to define your build tasks. Also it uses a more time and resource intensive file lifting process whereas Gulp uses a lightweight streaming technique. From what I can tell from everything I've read, Grunt is losing ground and many developers are abandoning it for Gulp. If you're new to all of this and you really only have the time or inclination to learn one of these, learn Gulp.
Yeoman (also known as Yo)
Yeoman is a scaffolding tool which can be used to start-up new projects based on specific client-side frameworks you want to use. Lets say you want to start a new Angular project and you want to use some core scaffolding or template code for that framework to get yourself up and running and without having to install and download all of these of other tools. Yeoman uses pre-built Generators to scaffold the particular type of app you want to build. Generators are installed with npm and you then scaffold your app using the yo command. Yeoman can be pretty handy as it attempts to simplify the whole process by wrapping up all of the other elements, Bower, NPM, Gulp or Grunt by installing them for you in a single Yeoman install. When you scaffold an app using the generator you want, it downloads all of the client-libraries you're going to need for that type of app via Bower and creates the configuration files or build code for either Grunt of Gulp that's going to automate the build processes.
Yeoman is a really nice tool to have and can certainly save you a ton of time at the set-up of your app but I'd advise you get to know the other tools first so you know what's going on under the hood first. Then you can move to Yeoman to simplify the whole set-up process. Or do it the other way around and use Yeoman first and don't bother to deal with the other stuff and then later learn the intricacies of Gulp and Bower. Whichever floats your boat.
0 notes
Text
Options For C# Mobile Development
A while ago I wrote a review/product comparison for the two main product options available for developing mobile & cross-platforms apps in C#, namely Xamarin and RemObjects C#. I wrote it originally last March shortly after RemObjects C# had launched, for both myself and for some colleagues who were asking about the choices they had for mobile development. While I intended to do so at the time I didn't actually get around to posting it to a blog so having gone back and updated it to reflect recent developments I thought now is the ideal opportunity to post it. I hope you find it useful :
Mobile Development Options For C#
RemObjects recently launched their long awaited "Hydrogene" product under the commercial name RemObjects C#. I've been quite looking forward to it's release especially knowing that RemObjects have a solid reputation and track record of producing genuinely excellent developer tools.
First a bit of background. RemObjects is a C# implementation of their Oxygene technology. Oxygene consists of an Object Pascal-style language front-end compiler and a back-end compiler. Their back-end compiler can build natively for the .NET framework (including Mono), Java (including Dalvik for Android) and the Cocoa/Cocoa Touch frameworks. This effectively means that you can use Oxygene to develop in Pascal for every flavour of Windows including WinRT and Windows Phone as well as for Android, iOS and Mac OSX.
RemObjects C# brings a new front-end compiler for the C# language to the party which builds for RemObject's existing Oxygene back-end compiler. This effectively brings C# to the world of Android, iOS, Mac OSX and of course Windows and the various flavours thereof. To avoid confusion, RemObjects have renamed their back-end technology to Elements and now both the Oxygene language and their C# implementation share Elements. Therefore although RemObjects C# is a new product, it actually builds on very mature and proven technology which has over a decade of development behind it.
RemObjects C#, as a new player in the C# mobile tools market, becomes a long-awaited competitor to Xamarin who, until now, seem to have had everything their own way. The lack of competition so far may also possibly explain their rather hefty license fees at $999 per platform, per developer, per year (they do also have a lower priced and even a free option although with serious and even crippling limitations). In comparison RemObjects C# comes in at a rather pleasant $499 for ALL platforms per developer per year. I should add that if you're a start-up still in your first couple of years and have revenues below a certain value you can contact Xamarin and they'll offer a discount. When I contacted them about this they offered a 20% discount.
Both RemObjects C# and Xamarin offer deep integration with Visual Studio as their primary IDE option. Xamarin does have the advantage of a cross-platform secondary IDE called Xamain Studio (a fork of the open-source MonoDevelop IDE) if you want to develop directly on the Mac. If you can't stretch to the Professional edition of Xamarin (pricing mentioned above) then Xamarin Studio is your only choice as VS integration is excluded from their free and cheaper editions (which has been a bone of contention for developers at small software shops who need VS integration but can't stretch to the high prices). RemObjects also have their own Mac-specific IDE, called Fire, which is now in advanced stage beta. From initial viewing, Fire looks impressive and may even potentially be a competitive advantage for RemObjects C#.
All of this means that if you want to target all 3 non-Windows platforms (iOS, Android and MacOS X) and use Visual Studio as your IDE, Xamarin is 6 times more expensive than RemObjects C#! So with such attractive pricing surely RemObjects C# is ready to swoop in and steal Xamarin's lunch? The answer however is not quite that simple. RemObjects C# and Xamarin are not like-for-like offerings although they basically produce the same end result, mobile apps written in C#. Each product takes a radically different approach to the other and puts emphasis on different priorities. Choosing which one is best for you will depend entirely on which product's approach matches your own priorities.
Both RemObjects C# and Xamarin are on the same page about one approach - they both agree that you should not be using their tools to create a single app to run on all platforms and are explicit that you will need to learn the different platforms and their APIs and, at the very least, develop separate and unique UIs for each platform to take advantage of the native interfaces and behaviours of each platform. If you were hoping to be able to write a single app and just recompile it for each platform then you've come to the wrong place with both of these tools. With that being said, the introduction of Xamarin Forms last year somewhat blurred Xamarin's approach to this principle by enabling you to code a single UI and have the framework provide the relevant native UI look-and-feel for the OS you are running on.
Xamarin is based on the Mono framework which is a cross-platform edition of the .NET framework. The founders of Xamarin took the Mono open-source project from Novell when it was acquired and put it under the wing of their new start-up. They continued to develop it with great verve and, importantly, great fidelity to the .NET framework. Therefore when you develop in C# with Xamarin, you are coding against virtually identical libraries, APIs and features of the .NET framework that most C# developers are used to. This is where Xamarin's real strength lies, in it's code re-use and close compatibility with .NET. While you may be required to develop different UIs and functionality for each platform, your business logic and data access code can be reused across all supported platforms, from Windows to Mac, from iOS to Android to Windows Phone.
This code reuse all comes at a price of course. Your Xamarin apps must always come bundled with the Xamarin-Mono runtime which makes them much larger than pure-native apps and potentially slower. Your code is always working through an abstraction layer provided by Xamarin and not the down-to-the-metal level of the platform's framework. This means you are always one level of abstraction away from the platform's framework. You are also entirely reliant on Xamarin to keep these abstractions up-to-date with platform updates (e.g. when Apple launched iOS 7) although they have proved to be impressively quick at doing so. It does mean however that if you decide that their high annual renewal prices are too rich for you then you're not going to get access to the future latest and greatest when new platform APIs are released. Don't underestimate how problematic this could be as mobile is moving rapidly and you'll be tied in to and paying Xamarin for a long time to come. As you'd expect Xamarin play this down but it shouldn't be overlooked. For example Apple require that any new app submissions or updates require compatibility with iOS 7 (by using the iOS 7 SDK). If you hadn't renewed with Xamarin when it provided those new API wrappers for iOS 7 then you're effectively barred from submitting updates for your app to the App Store. Whether this is an issue for you all depends on the size of your (or your boss') bank account!
RemObjects C# takes the exact opposite approach. It offers the ability to create pure-native apps which work directly with the underlying platform's framework. There is no level of abstraction involved and you are coding directly against the platform's framework libraries and APIs. When you take your C# app and compile it with RemObjects compiler, let's say for Android, it produces output which is executed (or rather JIT'ed) directly by the Dalvik virtual machine, just as if you'd developed it in Java using Google's tools, with no overhead of adding in an additional framework. When you compile an app for iOS, it produces output which is executable directly on the Cocoa Touch framework just as if you'd developed it in Objective-C (or Swift) using Xcode. This gives RemObjects C# apps the advantage of being lightweight, small and fast which the App Stores love. Also because you're always working with the core APIs directly and not through an abstraction layer you are not reliant on RemObjects releasing updates to get you up-to-date with the APIs for the latest versions of each platform and you're not being forced to pay them on-going if you don't want to. At $499, RemObjects' annual renewal is not a prohibitive cost and they provide good value for it - however it's always good to know that if you're cash-strapped when renewal time comes around you won't be effectively barred from access to significant platform and API changes.
All of this however comes at the expense of code sharing. As you're always working directly with platform APIs, if one framework doesn't have a feature of another you can't use it within any code that you want to be shared across platforms. This means that any calls to framework specific libraries, including .NET, are not possible in code that you want to share across all platform editions of your app. RemObjects claim this is an advantage because you're never working with the lowest common denominator and always directly & naturally with the platform you're targeting. For example the Cocoa Touch framework for iOS works completely differently than .NET and putting a wrapper around it to make it work just like .NET can stop you from taking full advantage of the many benefits and advantages of Cocoa and iOS. Therefore if getting to the core of what makes iOS (or Android) great and unique is important to you then RemObjects C# certainly delivers on this score.
RemObjects C# does include a cross-platform library called Sugar. Sugar enables you to use types and library calls which map (as opposed to abstract) to their native equivalents and therefore enable you to code against Sugar rather than any specific platform's framework. The RemObjects C# compiler will do the rest. Sugar is an attempt to provide a level of cross-platform code sharing for business logic while still compiling directly to the native framework and platform. It certainly helps but, unfortunately for RemObjects, it isn't the silver bullet which is going to blow Xamarin out of the water either. If you want to target all supported platforms then all business logic code that you want to share must call Sugar APIs and use the RemObjects C# compiler - even for Windows & Windows Phone development.
Therein lies the kicker for RemObjects. Until RemObjects C#, the C# language has been inextricably linked with the .NET framework or it's Mono sibling. Every C# developer calls libraries, features and APIs of the .NET framework in their code, it's like second nature. Xamarin embraces this. RemObjects C# on the other-hand decouples the language from it's original framework in order for you to use C# to code directly against the native framework of the platform you're targeting. Not using classes and APIs of .NET will, at the very least, feel odd to experienced C# developers. It's not necessarily a bad thing and can have it's advantages however it all depends on your perspective and your priorities.
As an aside, RemObjects C# .NET compiler can also target Mono so you can code against .NET/Mono for MacOS X and any other platform that has a Mono implementation such as Linux. However Xamarin now have commercial control over the previous MonoTouch (for iOS) and MonoAndroid open source mobile implementations of Mono and rolled them up into their Xamarin product so the RemObjects C# .NET offering can't reach these targets.
Despite the pure-native approach RemObjects have chosen they have, quite rightly, realised that LINQ is a hugely important part of a C# developer's toolkit and have built in support for it on all platforms regardless of the fact that it is not supported natively on anything other than .NET/Mono. This highlights a dilemma that the RemObjects guys may now face : Which features are part of the C# language and which are part of the .NET framework and does it really matter if the developer expects it to be in his C# toolkit regardless. Due to C#s link to .NET the lines are blurred, if not technically then at least in the minds of C# developers, and they may find that they have opened up the proverbial Pandora's box and may end up implementing much more of the .NET framework on the other platforms just to satisfy developers who expect it to be so. This is something RemObjects are going to have to think long and hard about in designing the future of their C# implementation - getting this balance right could be the key to their success.
So it is your job to determine your priorities - Do you want maximum code reuse across platforms and be able to code against the familiar .NET framework but pay the price of inflated app sizes, potential performance hits and a reliance on another vendor to provide you with library abstractions for access to the latest and greatest platform APIs & charge you a heavy premium for it. Or do you want a pure-native app which is lightweight and quick, to code against each platform directly and write an app for each platform which takes full advantage of the platform's framework and without having to rely on abstractions from the vendor but pay the price of limited code sharing across platforms and not being able to rely on coding against the familiar .NET framework. Money and budgets are also important and will factor into your decision when you consider that choosing the first option will cost you 6 times the price of the second (for equivalent functionality).
If money was no object or if Xamarin reduced their prices *significantly* and I needed to get an app out quickly across all platforms and wasn't too fussed about working directly with each platform's native framework and wasn't too concerned how big the app was then Xamarin would be my choice. If I had time to develop an app for each platform one-at-a-time, to take advantage of the platform's features and to really give the users a truly native experience on their platform of choice and submit lightweight apps to the app stores and I didn't have a ton of money to spend then RemObjects C# would be my choice. RemObjects C# appeals to the purist in me and Xamarin to the pragmatist.
I'm never too keen on spending money and it doesn't sit right with me when comparing 2 competing products that one is several times the cost of the other - if Xamarin is generally a "better" product, and by no means have I concluded that it is, is it really 6 times better than the alternative? The answer is an unequivocal NO! However I do think Xamarin's advantages of maximum code sharing and ability to code against the .NET framework across all platforms will appeal to the majority of C# developers and their bosses. Xamarin's partnership with Microsoft could also help push decisions in their favour too. Therefore I think the single biggest thing that Xamarin can do to cut RemObjects C# off at the knees is to lower their prices to somewhere near RemObjects' pricing by offering an affordable intermediate edition for smaller shops with VS integration. By not doing so and keeping their prices prohibitively high for smaller software shops (which is where the majority of their untapped market appears to be) they are opening the door for a competitor like RemObjects C# to sneak in and steal their lunch. On the other hand RemObjects have to focus on developing their offering to implement better code-sharing between platforms - whether this means developing key features from .NET that traditional C# developers expect or significantly expanding their Sugar cross-platform library or a combination of both. If they don't then there's a high possibility that Xamarin will do whatever it takes to mop up the market and leave RemObjects to pick up the crumbs. Which would be a shame because, as a typical Englishman, I always cheer for the underdog.
0 notes
Text
Update & Remove Firebase Data
In the AngularJS & Firebase quick-start demo, we covered adding and reading data to/from a Firebase database. With that done I'll complete the CRUD methods by covering how to edit and delete data.
In my previous post about using snapshot.forEach(), I explained how you can use Firebase's snapshot object when reading data to grab the unique id for each record which Firebase generates. You then assign this id to each corresponding object in your view-model array using snapshot.name() by iterating through the records using snapshot.forEach(). This is important as it gives you a reference on each object back to the source record. Once you have that reference, editing and deleting records is very straightforward.
Updating Data You pass the unique id, which the selected object in your data array maintains, into the child() method of the data reference. This returns a reference object to the actual data record.
var recordReference = dataRef.child(employee._id);
Now that you have a reference to the record you can simply call the update() method and pass it the object with the changes in.
recordReference.update({employeeName :employee.employeeName, employeeAge :employeeAge});
That's it! Pretty simple.
Deleting Data You start by doing the same as updating, by grabbing a reference to the record by passing the unique record id to the child() method on your data reference.
When you have your reference you simply make a call to the remove() method.
recordReference.remove();
Very simple. Obviously before calling either update() or remove() make a check to ensure that the child() method has returned an actual reference. If the record you're trying to update or delete no longer exists, the child() method will return null.
0 notes
Text
Manipulating Firebase snapshots - using forEach
Snapshots are Firebase's way for getting data into your app. This can be when initially retrieving data or when data that you're interested in is changed (to utilise Firebase's realtime data).
As explained in the quick-start video, you use Firebase's on() method to set a "listener" on the data that you're interested in :
$scope.dataRef.on()
on() expects a pre-defined type of listener passed as a string as the first parameter. There are several listeners available. In the demo we use 'value' which is a "catch-all" listener as it checks for new records or changes to any and all records in the collection you're referencing and delivers all records whenever there is a change.
The second parameter you need to pass to on() is a function which tells the listener what it needs to do when triggered. This function importantly receives a parameter called snapshot. Snapshot is an object which, as the video explains, contains the data which is passed back from the database. However the data is only one element contained within snapshot. Snapshot also contains some very useful tools to help you manipulate the data that has been passed back.
In the video we simply assigned the data passed from the listener into an object list on the $scope.
dataRef.on('value', function(snapshot) { $scope.employees = snapshot.val(); });
As the above shows, the data within snapshot can be accessed through calling the val() method. The above example however is very simplistic and while it's adequate to just display a list of data, in a real-world app it's unlikely that this will give you everything you need. For example you don't get a unique record identifier to later allow the user to select, edit, delete or lookup a particular record.
To get the unique id of each record we have to iterate through the data we receive and manually assign the keys we want into the employees list one at a time. For this we can use the forEach method on the snapshot.
snapshot.forEach receives a function as a parameter. In that function you carry out the task of assigning each record to your employees array. Your function must receive a parameter of item which handily contains everything about each record as you could possibly need - including the unique id - as it iterates through the list.
snapshot.forEach(function(item) { var itemVal = item.val(); };
As shown above, the data in the item object can also be accessed using the val() method. Once you have that object you can start pushing the data into a new object in your employees list array :
snapshot.forEach(function(item) { var itemVal = item.val(); $scope.employees.push({employeeName :itemVal.employeeName, employeeAge :itemVal.employeeAge}); };
However this doesn't give us much more than we had before, except extra lines of code. To make it useful we need to get the unique id of each record. The id isn't maintained within the actual val() data, instead we call the name() method on the item. This gives us that "weird", elongated record id we showed on the video.
_id : item.name()
We now have the same list as we had before with the addition of maintaining the id so we can manipulate that record elsewhere in our app. By utilising snapshot.forEach we were able to iterate through the records to get at those ids. Let's put it all together :
var $scope.dataRef = new Firebase("https://sweltering-heat-9999999.firebaseio.com/Employees");
dataRef.on('value', function(snapshot) { snapshot.forEach(function(item) { var itemVal = item.val(); $scope.employees.push({employeeName :itemVal.employeeName, employeeAge :itemVal.employeeAge, _id :item.name() }); }; }); Quick note : If you're following along with the example in the video, for this to work you need to also change the employees type from an object to an array eg
var $scope.employees = [];
We'll look at other things we can do with snapshot and with other Firebase listener types in future posts.
0 notes