Skip to content

podcast

Hacking New Year's Resolutions for 2015

As the year draws to a close and a new one begins on January 1st, many people set New Year's resolutions. However, 92% of New Year's resolutions ultimately fail. Why is that? Welcome back, listeners, to another episode of Continuous Improvement. I'm your host, Victor, and today we're diving into a topic that has captivated my curiosity - New Year's resolutions. We all strive to better ourselves, but why do so many of our resolutions fail?

Studies have shown that only 8% of New Year's resolutions actually succeed. That's a staggering number. So, I embarked on a mission to understand why and discover how we can defy the odds.

Researchers suggest that relying solely on willpower to achieve our goals may not be enough. Willpower can be depleted, leaving us feeling disheartened and less likely to continue our journey towards self-improvement.

But fear not, my fellow growth enthusiasts! There is a way to break this cycle and achieve meaningful change. The key lies in setting simple and achievable goals.

Let's break it down. Instead of aiming for lofty ambitions, focus on the low-hanging fruit. These small wins can provide the motivation we need to keep going. Remember, excellence is not a one-time accomplishment but rather a habit formed through consistent actions.

To illustrate this, let me share with you my annual objectives – the daily habits I've incorporated to keep myself on track.

First and foremost, it's always important to keep coding skills sharp. I dedicate at least one hour each day to solving toy problems or tackling coding challenges. Trust me, even a single hour of focused coding can take you a long way.

Next, physical health. We've heard it time and time again, but it's true – a healthy body fosters a healthy mind. I prioritize spending at least 30 minutes a day jogging or engaging in other exercises that keep me active and energized.

Learning should never cease, my friends. I allocate an hour each day to immerse myself in enriching content – whether it's reading books or watching educational videos. The power of knowledge is remarkable, and by dedicating time to learn each day, we unlock endless potential.

Finally, never underestimate the power of human connection. Strengthening social bonds is vital for our well-being. I spend an hour each day connecting with my loved ones – be it over a meal, a video call, or simply catching up on their day.

There you have it, the secret to continuous improvement lies in setting achievable goals and turning them into daily habits. Remember, it's not about making grandiose resolutions that fizzle out by February. It's about focusing on those consistent, small actions that compound over time.

I encourage you all to reflect on what areas of your life could benefit from a little tweaking and experiment with implementing these daily habits. Trust me, the results will speak for themselves.

That's a wrap for today's episode of Continuous Improvement. Thank you for tuning in, and remember, the journey toward self-improvement is one we embark on together. Stay curious, stay hungry, and never stop striving for growth.

Until next time, this is Victor signing off. Take care, my friends!

Angular.js Factory: Handling $http Asynchronously

The Problem:

Hello everyone and welcome to another episode of "Continuous Improvement". I'm your host, Victor, and today we are going to dive into a common problem developers face while using the $http service to retrieve data from a remote API.

In a recent blog post, the author shared a code snippet where the $http service was used to get data from an API. However, there was an issue that prevented the data from being returned back to the controller. Let's take a look at the code:

myApp.factory('myFactory', function($http) {
  var data = { anArray: [] };

  $http.get('/api').success(function(response) {
    data.anArray.push(response);
  });

  return data;
});

The problem here is that the return statement is executed before the $http GET request finishes pushing data into the array. This is a common mistake that can lead to unexpected behavior.

Thankfully, the author also suggests a solution to handle this asynchronous data retrieval issue. Have a look at the updated code:

myApp.factory('myFactory', function($http) {
  return {
    getData: function() {
      return $http.get('/api');
    }
  };
});

myApp.controller('MyController', ['$scope', 'myFactory', function($scope, myFactory) {
  $scope.data = [];

  var handleSuccess = function(response, status) {
    $scope.data = response;
  };

  myFactory.getData().success(handleSuccess);
}]);

In this updated code, the factory returns an object with a getData method that performs the $http GET request. The controller then calls this method and handles the eventual response using the handleSuccess function. This way, the data is correctly assigned to the $scope.data variable when it becomes available.

It's important to understand the difference between synchronous and asynchronous operations in JavaScript. Asynchronous operations, like retrieving data from a remote API, take time to complete. It's crucial to account for this and implement appropriate handling mechanisms.

That's it for today's episode of "Continuous Improvement". I hope you found this topic interesting and that it helps you avoid similar issues in your own code. If you have any questions or feedback, don't hesitate to reach out to me by email at victorleungtw@gmail.com.

Thank you for listening and until next time, keep coding and continuously improving.

Setting Up a Proxy Server with Express

The Problem:

Welcome to "Continuous Improvement," the podcast where we explore practical solutions to everyday problems. I'm your host, Victor. Today, we're going to talk about a common issue when working with APIs and how to overcome it.

Imagine you're working on a project that uses BreweryDB. You're trying to load some data from the API, but suddenly, you encounter a problem. The API doesn't support JSONP, leading to a CORS issue when you attempt to fetch data directly using Angular. Frustrating, right?

But fear not! I've got a solution for you. In this episode, I'll guide you step-by-step on how to set up an intermediate proxy using Node.js and Express. This will allow you to avoid exposing your API key and resolve the CORS issue.

Let's dive into the solution.

First, we need to install Express and Request. Open up your terminal and type in:

npm install express --save
npm install request --save

After the installations are complete, it's time to create a server.js file. In this file, we'll set up the proxy using Node.js and Express. Make sure you have it ready before moving on to the next step.

So, let's set up the route. Replace API_KEY with your actual API key. This step is crucial to ensure proper authentication.

Inside your server.js file, add the following code:

var express = require('express');
var request = require('request');
var app = express();

app.get('/api', function(req, res) {
  request('https://api.brewerydb.com/v2/?key=' + API_KEY, function (error, response, body) {
    if (!error && response.statusCode === 200) {
      console.log(body);
      res.send(body);
    }
  });
});

Great! Now that we have the route set up, let's move on to setting up the port.

In your server.js file, add the following code:

app.listen(3000);
console.log('Server running on port %d', 3000);

Superb! You're almost there. Now it's time to start the server. Open your terminal, navigate to the location of your server.js file, and enter the following command:

node server.js

Congratulations! You've successfully set up the intermediate proxy using Node.js and Express.

It's time to test your proxy. Open your browser and type in http://localhost:3000/api. If everything goes well, you should be able to see the JSON object and even log it in your browser console.

And that's it! You've overcome the CORS issue by setting up an intermediate proxy. Now you can fetch data from the BreweryDB API without any problems.

If you encounter any difficulties or have any questions, please feel free to send me an email at victorleungtw@gmail.com. I'll be more than happy to assist you.

Thank you for tuning in to this episode of "Continuous Improvement." I hope you found the solution helpful and can apply it to your own projects. Don't forget to subscribe to our podcast for more practical solutions to everyday problems. Until next time, keep improving.

Angular UI Bootstrap: Opening the First Accordion with ng-repeat

The Problem:

Welcome to "Continuous Improvement," the podcast where we explore solutions to common programming problems and help you become a better developer. I'm your host, Victor. In today's episode, we'll be discussing a problem I recently encountered with the accordion directive in Angular UI Bootstrap and the solution I found to make it work with dynamic content. So let's dive in!

The problem arose when I was using the accordion directive in Angular UI Bootstrap version 0.1.2. On the demo page, you can see an example where the first accordion group is opened by default:

<accordion-group heading="First Header" is-open="true"> </accordion-group>

This worked perfectly fine for static content. However, when I tried to generate dynamic content using ng-repeat, it didn't behave as expected. The accordion didn't open as intended. Let me show you an example:

<accordion-group heading="{{group.title}}" ng-repeat="group in groups" is-open="true"> </accordion-group>

But don't worry, I've found a solution to this problem!

Instead of using a static value for is-open, we can bind it to a property in our controller. Here's what the updated template looks like:

<accordion-group heading="{{group.title}}" ng-repeat="group in groups" is-open="status.isItemOpen[$index]"> </accordion-group>

And in the controller, we define the groups and the status object which will hold the isItemOpen property:

$scope.groups = ['First Header', 'Second Header', 'Third Header'];
$scope.status = { isItemOpen: new Array($scope.groups.length), isFirstDisabled: false };
$scope.status.isItemOpen[0] = true;

By using $index, we can dynamically set the value of isItemOpen for each accordion group based on their index. In this example, the first accordion group will be opened by default.

[BREAK]

If you prefer the first accordion group to be closed by default, simply change the value of true to false in the last line of the code snippet.

So, with this solution, you can now have dynamic content in your accordion groups and control their initial state using the is-open property.

And that's a wrap for today's episode of "Continuous Improvement." I hope this solution helps you overcome the problem with the accordion directive in Angular UI Bootstrap. If you have any questions or need further clarification, feel free to reach out to me. Thanks for listening, and until next time, happy coding!

Note on Ionic Framework: Android Platform in OS X

In this blog post, we will walk through several error messages that you might encounter when installing dependencies for the Android platform of the Ionic framework on Mac OS X. Follow this official guide to install Cordova and Ionic, and then create a project up until the step where you configure the platform.

Hello everyone and welcome to another episode of Continuous Improvement - your source for tips, tricks, and solutions to help you overcome hurdles in your development journey. I'm your host, Victor, and in today's episode, we will be discussing some common error messages that you might come across while installing dependencies for the Android platform of the Ionic framework on Mac OS X.

But before we dive into that, I want to remind you to check out our official guide at ionicframework.com/docs/guide/installation.html. It provides a step-by-step walkthrough for installing Cordova and Ionic, as well as creating a project up until the point where you configure the platform.

Alright, let's get started!

So, you've followed the official guide and reached the point where you need to enable the Android platform by running the command:

ionic platform add android

But hold on, you might encounter an error message that says:

Error: ANDROID_HOME is not set and "android" command not in your PATH.

What does this mean? Well, it simply means that the Android command is not recognized by your system because the necessary environment variables are not properly set. But fret not, I'm here to guide you through the process of resolving this.

Step 1 is to download the Android SDK. You can find the link to the Android developer website in our blog post at [link]. Once you're there, go ahead and download the stand-alone SDK Tools.

Step 2 involves unzipping the downloaded file. Make sure to choose a suitable location for the SDK. For me, I extracted it to my workspace directory at:

/Users/Victor/Workspace/android-sdk-macosx

But you can choose a different location if you prefer.

Now that we have the Android SDK set up, it's time for Step 3: setting the Android command in your PATH. This step is crucial for your system to recognize the Android command as a valid command. So, open your terminal and follow along.

First, let's make sure your .bash_profile ends with a newline:

echo >> /Users/Victor/.bash_profile

Next, we need to set the ANDROID_HOME environment variable in your .bash_profile file. This can be accomplished with the following command:

echo "export ANDROID_HOME=/Users/Victor/Workspace/android-sdk-macosx" >> /Users/Victor/.bash_profile

Remember to replace "Victor" with your own username.

In addition to the ANDROID_HOME variable, we also need to update the PATH variable. This ensures that your system knows where to find the Android tools and platform tools. Execute the following command:

echo "export PATH=${PATH}:$ANDROID_HOME/tools:$ANDROID_HOME/platform-tools" >> /Users/Victor/.bash_profile

Great, we're almost there! Now, in order to apply the changes we made to the .bash_profile file, we need to reload the terminal. You can do this by executing the command:

. /Users/Victor/.bash_profile

Phew, we've made it through Step 3! Now we're ready for Step 4, which involves installing Android-19. If you try running the command:

ionic platform add android

You might come across the following error message:

Error: Please install Android target "android-19".

To resolve this, we need to open the SDK manager by running the android command from your terminal:

android

Once the SDK manager is open, check the box for Android 4.4.2 (API 19) and click on "Install 8 packages…". Then, simply accept the license and patiently wait for the download process to complete.

Now, it's time to give it another shot! Run the command again:

ionic platform add android

And voila! Hopefully, you will see the reassuring message:

Project successfully created.

And there you have it, folks! You've successfully resolved the error messages that can occur while installing dependencies for the Android platform of the Ionic framework on Mac OS X. If you experience any issues throughout the process, don't hesitate to reach out to me via email at victorleungtw@gmail.com.

That wraps up another episode of Continuous Improvement. I hope you found this information helpful and that it saves you time and frustration in your development journey. Make sure to subscribe to our podcast for more valuable tips and solutions. Until next time, happy coding!

Configure CircleCI with Karma Test

Welcome to "Continuous Improvement," the podcast where we discuss solutions to common programming and development challenges. I'm your host, Victor. Today, we're going to delve into an issue that many developers face when setting up continuous integration with CircleCI and Karma tests for Angular on Heroku.

So, picture this: you've been working hard on your Angular project, and you're ready to set up continuous integration to ensure that your code is always tested and deployed smoothly. You choose CircleCI as your CI/CD platform because of its popularity and great features. You follow all the necessary steps, configure your build process, and run your tests locally. Everything seems to be working fine until you push your code to CircleCI. Suddenly, your tests fail, and you're left scratching your head.

Let me paint you a clearer picture. The error message you see on CircleCI says, "Uh-oh, some tests have failed! Failing command: npm test. Exit code: 1. Output: > karma start karma.conf.js. sh: 1: karma: not found. npm ERR! Test failed. See above for more details." Frustrating, isn't it?

But fear not, my friends! I have a solution for you. After spending some time digging through forums and consulting the documentation, I found a way to resolve this issue.

To get Karma to work on CircleCI, you'll need to make a small tweak to your CircleCI configuration file, commonly referred to as the circle.yml file. This file specifies the build process and dependencies required for your project.

Here's what you need to do: open your circle.yml file and add the following lines of code:

dependencies:
  pre:
    - npm install -g karma-cli
  cache_directories:
    - ~/.npm

By adding these lines, you're instructing CircleCI to install karma-cli globally before running your tests. This ensures that the karma command is available and avoids the dreaded "karma not found" error. Additionally, the cache_directories line ensures that npm dependencies are cached for faster builds.

Once you make these changes and push your code to CircleCI, you should see your tests running successfully, without any errors. And that's it! You've conquered this challenge and brought continuous integration to your Angular project seamlessly.

Thanks for tuning in to this episode of "Continuous Improvement." I hope you found the solution to the CircleCI and Karma issue helpful. Remember, as developers, we encounter problems all the time, but it's through continuous learning and improvement that we grow. Join me next time as we explore more programming challenges and their solutions.

How to Upload Files with Meteor.js?

Welcome to another episode of Continuous Improvement where we discuss ways to enhance your development skills and solve common coding challenges. I'm your host, Victor. Today, we're going to talk about handling file uploads in a Meteor project.

Last week, a friend of mine ran into an issue when deploying his Meteor app with a file upload feature. It worked perfectly fine on his local machine, but once deployed, the server kept refreshing and failed to load any page. After investigating this issue, I found a solution that I want to share with you all.

The problem arises when using cfs:filesystem, which uploads the file to a public folder directory. This is not allowed by the server for security reasons, resulting in the page failing to load. However, we have a workaround to solve this issue by using GridFS as a storage adapter to insert files into MongoDB.

Let's go through the steps to implement this solution in your Meteor project.

First, you need to install the necessary packages. Replace cfs:filesystem with cfs:gridfs in your project.

Open your terminal and enter the following commands:

meteor add cfs:standard-packages
meteor add cfs:gridfs

Great! Once you've added the required packages, you can move on to the syntax changes. Instead of using FS.Collection, switch to FS.Store.GridFS when declaring your collection.

var imageStore = new FS.Store.GridFS("images");

Images = new FS.Collection("images", {
 stores: [imageStore]
});

Now, let's configure the permissions for your collection. Add the following deny and allow rules based on your requirements.

Images.deny({
 insert: function(){
 return false;
 },
 return false;
 },
 remove: function(){
 return false;
 },
 download: function(){
 return false;
 }
});

Images.allow({
 insert: function(){
 return true;
 },
 return true;
 },
 remove: function(){
 return true;
 },
 download: function(){
 return true;
 }
});

Moving on to the user interface. In your client template, add a file input button for users to select their file.

<input type="file" name="..." class="myFileInput">

Now, let's handle the file upload event in your template.

Template.Profile.events({
   'change .myFileInput': function(event, template) {
      FS.Utility.eachFile(event, function(file) {
        Images.insert(file, function (err, fileObj) {
          if (err){
             // handle error
          } else {
             // handle success depending on your needs
            var userId = Meteor.userId();
            var imagesURL = {
              "profile.image": "/cfs/files/images/" + fileObj._id
            };
            Meteor.users.update(userId, {$set: imagesURL});
          }
        });
      });
   },
});

Don't forget to set up the publication and subscription if you have removed the autopublish package.

Meteor.publish("images", function(){ return Images.find(); });

Subscribe to it in your iron:router to ensure that you have the necessary data when rendering the template.

Router.route('/profile',{
 waitOn: function () {
 return Meteor.subscribe('images')
 },
 action: function () {
 if (this.ready())
 this.render('Profile');
 else
 this.render('Loading');
 }
});

That's it! By following these steps, you can successfully handle file uploads in your Meteor project, even when deployed to a server. If you're using an Amazon S3 bucket, consider using cfs:s3 as the adapter. And as a last resort, consider Filepicker as an alternative approach for file uploads.

I hope this solution helps you overcome any file upload challenges you may encounter in your projects. Remember, continuous improvement is key to becoming a better developer.

Thank you for listening to this episode of Continuous Improvement. Stay tuned for more tips, tricks, and solutions to enhance your coding journey. I'm Victor, your host, signing off.

Q&A with General Assembly Hong Kong

Welcome to Continuous Improvement - the podcast where we delve into the world of web development, share stories, and explore ways to continuously improve our skills and projects. I'm your host, Victor, a software engineer with a passion for coding and a thirst for knowledge.

In today's episode, we'll be discussing my experience in the Web Development Immersive course at General Assembly Hong Kong. But before we dive in, let me give you a brief overview of what I'm currently working on.

At the moment, I'm involved in several exciting projects that make use of JavaScript frameworks. One project is a native iOS/Android mobile app using Ionic and the Neo4j graph database. Another is a video chatroom built with WebRTC, Node.js, and Express.js. And then there's a music visualizer using WebGL and Three.js. Additionally, I'm working on a LinkedIn-like network platform using Angular.js and MongoDB, as well as a real-time voting system using Meteor.js and D3 data visualization. Some of these projects are open-source, so if you're interested in contributing or checking out demos, head over to my GitHub page at github.com/victorleungtw.

Now, let's rewind a bit and talk about my decision to enroll in the Web Development Immersive course at General Assembly. Before diving into web development, I was actually a digital marketer handling social media promotions in Australia. This job sparked my curiosity about how technology is transforming traditional media and marketing channels. Realizing the significance of having a strong web presence, I wanted to develop my coding skills. I chose the WDI course at General Assembly because I wanted an education that matched the pace of cutting-edge technologies.

Moving on, I want to share some insights about the student experience at General Assembly. One of my favorite aspects of the WDI course was the sense of camaraderie among students. We all came from different backgrounds, but we helped and supported each other both technically and emotionally. Building a website is a team effort, no matter your skill level.

Now, let's talk about how the course helped me achieve my goals. My primary objective was to secure a job in the web development industry, and General Assembly's strong network in Hong Kong greatly facilitated that process. I actively networked and participated in various events, such as hackathons. I'm grateful for the support of Justin, who played a significant role during this period.

Throughout the course, I learned countless lessons, but let me share the top three takeaways with you.

Lesson number one is the importance of wireframing. Initially, I underestimated the significance of planning ahead. But as I gained more project experience, I realized that taking the time to wireframe can save us a lot of time in the long run.

Lesson number two is all about user testing. It's crucial to continuously gather feedback from users. After all, code should be driven by market demand and user needs, not solely by what a developer thinks is cool.

Lastly, lesson number three is learning how to learn. The WDI course provided a strong foundation, but technology is ever-evolving. This means that ongoing self-directed learning is crucial to stay on top of the game.

Now that I've completed the course, you might be wondering what's next for me. Well, I live by the principle of "Always Be Coding." The more we code, the better we become. Currently, I'm focusing on strengthening my theoretical foundation to prepare for leadership positions within the IT industry.

And that's a wrap for today's episode of Continuous Improvement. Thank you for joining me on this journey as we explore the world of web development and share our experiences. If you have any questions, suggestions, or topics you'd like us to cover in future episodes, feel free to reach out to me on social media. You can find me on Twitter and LinkedIn. Stay tuned for more episodes focused on continuous improvement in the world of web development. Until next time, keep coding and keep improving!

Does Hack Reactor Make You Smarter?

Welcome to Continuous Improvement, the podcast where we explore personal growth and development through the lens of daily routines. I'm your host, Victor, and today we're diving into the world of software engineering and the impact it can have on our intelligence.

Have you ever wondered how software engineers create exceptional products? It's not just about writing code. It's about having the right mindset and continuously improving our skills. But where do we learn these skills? Is it just from books and online videos?

In a recent blog post titled "The Daily Routine", the author shares their experience as a remote student studying to become a software engineer. They start their day at 12 a.m., being 16 hours ahead of San Francisco time in Hong Kong. Talk about dedication! The course material covers complex topics like recursion, hash tables, and various frameworks. But what stands out is their focus on the daily routines that help them become a better software engineer.

One key aspect highlighted in the blog post is the importance of thinking critically. Being a skilled coder is just one part of the equation. Professional software engineers are fast and consistent learners, effective communicators, and motivated problem solvers. These skills are developed through hands-on practice, observing instructors, and learning from mentors. It's not just about learning from textbooks; it's about experiencing the real challenges of coding.

Another interesting point raised is the value of learning from smarter peers. The author admits feeling inadequate and experiencing imposter syndrome when surrounded by intelligent individuals. But instead of discouragement, the author uses this as an opportunity to learn at a faster rate. It's about embracing the discomfort of being around more knowledgeable peers and growing from that experience.

So, does coding actually make us smarter? The author believes it does and I couldn't agree more. The mindset we adopt and the people we surround ourselves with have a direct impact on our growth. As the saying goes, "You are the average of the five people you spend the most time with." When we interact with intelligent individuals, we push ourselves to think differently, to explore better solutions, and ultimately increase our own intelligence.

And that's a wrap for today's episode of Continuous Improvement. We've explored the daily routines of a software engineering student and how coding can enhance our intelligence. Remember, it's not just about the technical skills, but also about our mindset and surroundings.

If you enjoyed this episode, be sure to subscribe to Continuous Improvement on your favorite podcast platform. And don't forget to leave a review and share it with your friends. Stay curious, keep learning, and always strive for continuous improvement.

[End]

Testing with Mocha: Array Comparison

Hello and welcome to Continuous Improvement, the podcast where we explore common development issues and find solutions for them. I'm your host, Victor, and today we're going to talk about an interesting problem that I encountered while writing a Mocha test suite for array comparison.

So, I had this simple test suite for array comparison that should return true if two arrays have the same values. Here's the code:

describe('Array comparison', function () {
  'use strict';
  it('should return true if two arrays have the same values', function () {
    var myArray = ['a', 'b', 'c'];
    expect(myArray).to.equal(['a', 'b', 'c']);
  });
});

Now, you would expect this test to pass, right? After all, the arrays have the same values. But, to my surprise, the test failed with an AssertionError. Here's the error message:

AssertionError: expected ['a', 'b', 'c'] to equal ['a', 'b', 'c']

So, I started digging into the problem and here's what I found. Arrays in JavaScript are considered as objects in terms of their data type. That's why when we use the to.equal assertion in Mocha, it checks if the two operands are the exact same object, not just semantically equal.

Understanding this, I came up with a few possible solutions. The first one is to use .eql, which stands for "loose equality". This allows us to deeply compare the values of the arrays. Another option is to use .deep.equal, which checks if the operands are equivalent but not necessarily the same object. Alternatively, you can also check the .members in the array instead. And lastly, you can convert the array to a string and then compare.

Now, if you're interested in exploring these solutions further, I highly recommend checking out the references I found helpful. They are the ChaiJS BDD API Arguments Section and the ChaiJS BDD API Members Section.

And that concludes today's episode of Continuous Improvement. I hope you found this discussion insightful and helpful for your own development journey. If you have any topics or issues you'd like me to cover in future episodes, feel free to reach out to me.

Thank you for listening, and until next time, keep improving!