Tuesday 24 April 2018

Plugging those Knowledge Gaps

Programming languages. Frameworks. Databases. Development Methodologies. There's a lot of knowledge out there. Some of which you may have picked up, some of which you use often, much of which you have yet to even discover. One of the biggest mistakes a web developer - or any kind of software engineer, for that matter - can make, is to think that he knows enough. It is not enough. It is never enough. As a dev, you are not a finished product. You are a work in progress. Keep that in mind, and never let that go... because the moment you do, you are done. Finito.

Web technology is constantly in motion, and if you stay still, you will be left behind. That is not a warning. That is a fact.

And above all, don't ever make the mistake of thinking you're accomplished enough that basic knowledge is beneath you. I've encountered developers who thought they were hot shit, and turned their noses up at researching the basics. The end result is that they could write code that somewhat worked... but they pissed off their co-workers by leaving unhelpful comments in the code and writing code that was so ugly it hurt my eyes.

No dev is ever too good to revisit the basics, or improve.

Piecemeal Knowledge

Many experienced web developers don't learn their craft in school. We pick up stuff on the job, learning whatever needs to be learned and improving as we go along. That seems to be the optimal way because web technologies are widely varied, and it's hard to find two seasoned web devs with identical skillsets. Our knowledge tends to be piecemeal. There are too many things that need to be learned, and far too little time to master any one thing. We learn enough to achieve one task, then move on to the next. If - and only if - we have to execute the same task over and over, invariably we learn different ways of doing it, and a certain degree of mastery sets in.

That said, this is the very reason why there doesn't seem to be a standard set of skills any web dev should have. Sure, most if not all web devs know HTML, CSS and JavaScript. But to what extent? Many web devs would be proficient in at least one back-end scripting language, if not several. And probably worked with databases. How deep should that knowledge be? Most web devs would know enough HTML, CSS and JavaScript to build a web page. Not every dev has ever had to deal with shopping carts. Or Social Media widgets. Some have even managed to go through their entire careers without needing to call a single external API. Some will know just enough JavaScript to manipulate the DOM. Some will know just enough CSS to format text and change page backgrounds. Some will know just enough PHP (or Python, or C#, take your pick) to run a few automated tasks and make some calculations.

That is not impossible, or even wrong. It happens.

No one has an answer to exactly how much a web developer should know, other than a very general list of things... and if they do, that answer is invariably incomplete. That is because web technology is used in so many contexts that there is no one skillset that will cover everything, unless one learns everything. And common sense will tell you that it is impossible. Some people will consider you a total noob if you don't know how to BCNF a relational database. Or the difference between an associative array and a hash table. Or all seven layers of the OSI model. But really, practically, how often in a typical web development environment does a developer have to know these things? If you learn these things and don't use them often enough, how long before you forget them? For instance, I always have to look up how to write a simple Switch Case statement in any language - because I use these maybe once a year. Or less. But a Try-Catch or a For loop - now these are on the forefront. But are these examples true for you? Are they true for all devs?

Whack-a-Mole

There are knowledge gaps everywhere. They will always exist. Everyone has them. Plugging these knowledge gaps is like playing that Whack-a-Mole game - as soon as you bop one pesky mole on the head, another pops right up! Because the more you learn, the more stuff you discover that you've yet to learn.

It's an impossible task. It'll keep you busy for eternity. Does it mean that we shouldn't do it?

Fuck, no!

You see, plugging these knowledge gaps is what keeps devs like me going. The nice thing about web development is that there's always something new and fun to learn. The sucky thing about it is that there are only twenty-four hours a day to learn stuff with.

How to plug those knowledge gaps?

As a fellow professional of mine likes to say, "Google is your friend". Every time you come across a term you haven't heard before, look it up. Religiously. Your search may be satisfied right away, or it could lead you down an endless rabbit hole. Either way, this is a good thing.

Every time you make a discovery while tinkering away, confirm that discovery. Search for the countless other people who have undoubtedly trodden the same path. See what they have to say, and add what value you can. Because silence may be golden, but sharing is priceless.

The takeaway

Generally, you need to start having at least a vague idea of what you don't know. It's all right to be an idiot. Just don't be the kind of idiot who doesn't know he's an idiot.

The only thing you can do about knowledge gaps, is to keep on identifying and rectifying them. You can never eliminate all your knowledge gaps - but you can ensure that the knowledge gaps that you have today, aren't exactly the same ones you had last year. 

Keep plugging away,
T___T

Wednesday 18 April 2018

Spot The Bug: Full Transparency

Welcome back, you bug-ridden programmers.

I spy bugs with my little eye...

Today's Spot The Bug edition will deal with one of the pitfalls of client-side scripting. I used to be very sure of what was going on with the Document Object Model (DOM) until this happened to me.

I was just going for a simple Proof Of Concept. There was supposed to be a square in the middle of the page, orange in color. Upon clicking the square,  once, it was supposed to become 50% transparent. And if I clicked on the semi-transparent square a second time, it was supposed to become fully transparent, i.e. invisible.

Here's the HTML code and JavaScript.
<!DOCTYPE html>
<html>
    <head>
        <title>Opacity Test</title>

        <style>
            .square
            {
                width: 200px;
                height: 200px;
                border: 1px solid #000000;
                cursor: pointer;
                margin: 10% auto 0 auto;
            }
        </style>

        <script>
            function adjustOpacity(obj)
            {
                var bgcolor = obj.style.backgroundColor;
                var opacity = parseFloat(bgcolor.replace("rgba(255,100,0,", "").replace(")", ""));

                if (opacity > 0)
                {
                    opacity -=0.5;
                }

                obj.style.backgroundColor = "rgba(255,100,0," + opacity + ")";
            }
        </script>
    </head>

    <body>
        <div class="square" style="background-color:rgba(255,100,0,1)" onclick="adjustOpacity(this);">
            Click me
        </div>   
    </body>
</html>


So, I had this bright idea to simply grab the transparency values from the DOM, meddle with the Alpha value and alter the DOM. No need to produce a lot of If cases, right? Sounded good in theory...

What went wrong

It simply didn't work. The square remained stubbornly, solidly orange no matter how many times I clicked it.


Why it went wrong

See, what happened was that I had coded my CSS inline, which is usually a bad idea anyway. What the browser (in this case, Chrome) did was to parse the HTML and build the DOM with the appropriate properties, making little simplifying tweaks along the way. I did a little test...
        <script>
            function adjustOpacity(obj)
            {
                var bgcolor = obj.style.backgroundColor; console.log(bgcolor);
                var opacity = parseFloat(bgcolor.replace("rgba(255,100,0,", "").replace(")", ""));

                if (opacity > 0)
                {
                    opacity -=0.5;
                }

                obj.style.backgroundColor = "rgba(255,100,0," + opacity + ")";
            }
        </script>


Which meant the background-color property ended up looking like this...


You see now why my JavaScript didn't work? It was looking for a pattern that didn't exist.

How I fixed it

Man, that took a lot of fixing. First, I defined three classes - full, half and none.
        <style>
            .square
            {
                width: 200px;
                height: 200px;
                border: 1px solid #000000;
                cursor: pointer;
                margin: 10% auto 0 auto;
            }

            .full
            {
                background-color: rgba(255, 100, 0, 1);
            }

            .half
            {
                background-color: rgba(255, 100, 0, 0.5);
            }

            .none
            {
                background-color: rgba(255, 100, 0, 0);
            }
        </style>


Then I fixed the JavaScript to change the class name instead of the transparency values, after commenting out my previous code.
        <script>
            function adjustOpacity(obj)
            {
                /*
                var bgcolor = obj.style.backgroundColor;console.log(bgcolor);
                var opacity = parseFloat(bgcolor.replace("rgba(255,100,0,", "").replace(")", ""));

                if (opacity > 0)
                {
                    opacity -=0.5;
                }

                obj.style.backgroundColor = "rgba(255,100,0," + opacity + ")";
                */

                var bgclass = obj.className.replace("square ", "");

                if (bgclass == "full")
                {
                    bgclass = "half";
                }
                else
                {
                    if (bgclass == "half")
                    {
                        bgclass = "none";
                    }
                }

                obj.className = "square " + bgclass;
            }
        </script>


Plus, some changes to the HTML.
    <body>
        <!--<div class="square" style="background-color:rgba(255,100,0,1)" onclick="adjustOpacity(this);">-->
        <div class="square full" onclick="adjustOpacity(this);">
            Click me
        </div>   
    </body>


And yep! It worked just fine now. This is how it looks after the first click...


And after the second click.


Conclusion

Try not to get cute with the DOM. Remember that all web development is basically writing code for browsers to execute, and as such, you're always at the mercy of the browser.

Also, avoid inline styling. Use CSS class names!

Stayed tuned for the next Spot The Bug. Be there or be square!
T___T

Saturday 14 April 2018

Tech Terms and why we should use them right

Tech is full of terms specific to the industry only. Buzzwords, jargon, phrases - they're all part of the tech terminology. Terminology is important.

Years ago, I was in a company that used Team Foundation Server (TFS) for version control. I never knew what it was called. Sure, I knew that it was called "TFS" and that I had to check in my code after I was done with it, but I never knew the specific term for this. And it came back to haunt me years later when interviewers asked me if I had ever used a code repository. It never occurred to me, until much later, that a "code repository" was this thing called "TFS" which my team used to collaborate on code, and check on different versions of the code base. I had been using it for a year and didn't even know this much! All I had been doing was working day in and out without any thought to the larger picture. Like it or not, this suggested a lot of things about my professionalism, none of them particularly flattering.

This is not the only example. For years, I worked on CSS without understanding the term "CSS Box Model". I wrote code in a certain way to guard against threats, not knowing what I did was "whitelisting". And I never knew my preferred style of placing curly brackets was known as the K&R style. I just wrote code, and I knew it worked. End of story.

See, this is why terminology is important. Using the correct terms puts you in the same league as the professionals. You share a language. Instant cred.

This is a double-edged sword, though...

The problem with instant cred

You see, plenty of people have figured out the path to "instant cred" too. Pepper their dialogue constantly with tech buzzwords that ultimately don't mean anything in the wrong context. Drop jargon like it's going out of fashion. Join conversations they have no business joining because they haven't done the damn work. These are the pretenders who are trying to sound like they know the industry without having worked a single day in it. They don't have to know anything; just sound knowledgeable.

"So we simply need a Separation Of Concerns in order to follow the Don't Repeat Yourself Rule and Stand On The Shoulders Of Giants to avoid Duplication of Effort."

Feel nauseous yet?

*urk*

When I was younger, I tended to think the world was full of these snake-oil salesmen. In fact, anyone who used terminology too liberally, was immediately classified as some poseur who was trying way too hard to impress people. Maybe get promoted to management or something.

Well, I wasn't completely wrong then, but neither was I completely right. The world is full of these poseurs. But that does not negate the importance of using the right terminology.

Earlier this year, I talked about the difference between parameters and arguments. A lot of techs I know like to say things like "pass in those parameters to the function", which is incorrect. These techs aren't stupid or incompetent; but like me, their training was not totally structured. Whatever we knew, we picked up along our career paths. Bits and pieces of a large scattered picture. The correct terminology wasn't important as long as we understood each other.

To some extent, that works. But it's hard to be effective at what you do if you can't communicate, and you can't count on being in the same team all your life. You will work with other teams who will use the correct terminology, and you will end up confusing the living heck out of each other.

Being pedantic much?

No, I'm not advocating being pedantic at all. In fact, I have nothing but disdain for people who keep correcting me to nitpick on a word I might have incorrectly used when trying to explain a concept, especially when they know damn well what I meant.  Collect your achievement award and get over yourself, why don't you?

But making an effort to use the correct terms is important because it tells people you're not a total idiot. Especially when you're not a total idiot. Don't let a couple of incorrectly used words detract from the brilliance of your ideas. That's all I'm saying.

Using the correct terms gives you insight into the bigger picture. It makes you think about what you're doing instead of simply doing it, which can be dangerous.

Don't take things to extremes though...

There's a happy middle ground in all of this. You don't have to drop buzzwords just to show people you know your shit, but you do have to understand technical terms because, ultimately, they're one of the things separating you, the professional programmer from the rest of the garbage.

The solution to this? Don't just code. Read. Question. Learn.

Till next time, one good term deserves another!
T___T

Tuesday 10 April 2018

Five Dimensions of Competency applied to Web Development

According to the Workforce Skills Qualification initiative in Singapore, there are five dimensions to competency - Task Skills, Task Management Skills, Contingency Management Skills, Job Management Skills and Transfer Skills. And today, I would like to apply this to a web development context.

Why? What's the point?

Because contextualizing stuff helps me learn better. (Yes, this is one of my many, many school projects).  Plus, I like writing listicles. Let's go!

KSA

Before we continue, we've first got to understand what role KSA plays in the Five Dimensions. of Competency. A competency in any field is a measurable set of Knowledge, Skills and Attitudes behind every individual's performance. Therefore, every dimension of competency has its own related KSA.

Knowledge is the theoretical or practical know-how. It's your understanding of the subject matter - namely the why and how.

Skill is the psychomotor capability developed through training and/or experience. It's your ability to perform what you know.

And Attitude is your predilection towards doing it right. It mostly comprises of your values and your outlook. Basically, knowing that you should do something is not the same as being inclined to do it.


1. Task Skills

This is pretty straightforward. It refers to all the little tasks you have to perform while doing your job. It measures your ability to carry out a single task to professional standards.

Take for instance, writing a HTML layout off a wireframe. Is your final product responsive? Is it W3C Compliant? Cross-browser compatible?


Setting up a database.

Or, setting up a database. Are the fields aptly named? Is the schema appropriate? Fully, or at least adequately normalized? Efficient and robust?

Knowledge - Understanding compliance standards, why databases need to be structured a certain way, etc.

Skill - Being able to create code structures, schemas, HTML layouts, etc.

Attitude - Commitment to standards compliance and best practices.


2. Task Management Skills

This one takes it up a notch. It's an assessment of how well you coordinate all the individual tasks into a work activity.

Writing a web application, or even adding a new feature to an existing web application is a work activity that is made up of several individual tasks. Now, when performing that particular work activity, you're expected to architect the solution, cater for existing use cases and anticipate possible edge cases. You may need to leverage off existing data storage and probably write a fair amount of code. How well you do those tasks is not the issue here - what you're assessed on, is how well you prioritize what needs to be done first, and in what sequence. You may decide that the database is more important to you, but users are naturally going to want to look at the interface first. How you resolve these conflicts is also a measure of how aptly you are managing this activity.

Juggling it all.

Let's say you decide to work on the interface first, and manage to set up a fairly workable layout. Do you then spend more time making the layout responsive, or do you decide that displaying data takes priority? Again, it's not how well you perform these individual tasks, but how well you manage doing all this at once.

Knowledge - Understanding how different tasks relate to each other in the overall scheme of things.

Skill - Prioritizing, time management, self-organization.

Attitude - Having the discipline to stick to a plan, as opposed to just "going with the flow".


3. Contingency Management Skills

Things are going to go wrong at some point. It's one of the undisputed and immutable laws of the universe. When things go wrong, or don't perform as expected, how do you handle it? How good are you at dealing with unusual situations that arise?

Handle problems like a pro.

In web development, like any other software development, things can (and will) go wrong. The layout may turn out to totally fail when testing in Internet Explorer. The data might take a little longer to load than expected. The data displayed might even be wrong. How solid are your solutions? How quickly do you find the root of the problem?

Knowledge - Understanding how to root out problem sources, interpreting error logs and generally how to troubleshoot.

Skill - Applying fixes and solving problems.

Attitude - Commitment to solving the problem itself rather than just a surface fix (and of course, knowing the difference!).


4. Job Management Skills

Some things aren't just limited to your role alone. Very few workers function in a vacuum. You have to learn to play well with others, and interact with other cogs (of which you are one) in the entire system.

Is your code easy to read, modify and extend by your team members? Are your comments useful? Do you coordinate and communicate effectively with your teammates?

Are you a team player, or a go-it-alone cowboy?

Are you able to interact with Infrastructure for support when you need to deploy your web app? Or discuss design issues with the Design department to better facilitate the building of the interface?

Pair programming.
These questions are pertinent because the days of one super programmer doing everything by himself, are just about over. No matter how good you are, if you can't work well with others, you're pretty much useless to any organization. I'm not saying people have to like you. But they do have to like working with you. There's a distinction to be made there.

Knowledge - Understanding why your ability to relate to your co-workers, while not technical, is still vital. Use of Code Repositories, Pair Programming and Code Reviews.

Skill - Diplomacy, communication.

Attitude - Respecting the contributions of others in the final product and recognizing that everyone is important.


5. Transfer Skills

This measures your ability to apply your knowledge outside of the usual context. Knowledge and skill are limited in utility if they can't be applied to new situations.

Let's say you can write a web application like a boss. How well can you translate that to writing a mobile application?

If you can make a web app,
can you make a mobile app?

Or perhaps, you can implement stacks, heaps and all manner of cool magic using Java. How effective would you be if you had to use PHP instead? Is your foundation in programming strong enough that any language will do?

Knowledge - Understanding the different components of your knowledge, breaking them down and applying them to fresh scenarios.

Skill - The ability to accomplish different things using the same tools, or accomplish the same things using different tools.

Attitude - Being willing to drive innovation by applying your skills to newer areas. Being willing to learn new tricks, or new tools.


All in all...

There's a lot more to competency than simply being good at something, or being able to do something to a certain standard. If you can't manage your own workload, react and recover from unexpected situations, communicate with others or apply your skills to a different environment, then you're not competent enough to be truly useful. And if you're an actually talented individual otherwise, that would be a huge pity. It's not enough to be good at individual tasks because machines will beat you at this every time. To be truly competent, you have to measure your competency in these five dimensions.

Most competently yours,
T___T

Thursday 5 April 2018

Web Tutorial: Multilingual Easter Form (Part 3/3)

Now to send some email!

To do that, we first have to add a form opening and closing tag to your about View. Use these helpers. The route is sendmail, and the method, which is POST, needs to be specified.

about\index.html.erb
<div class="lblAbout">

</div>

<hr />

<%= form_tag("/sendmail/", method: "post") do %>
  <%= label_tag(:txtName, "Name", class:"lblName") %>
  <%= text_field_tag(:txtName) %>
  <br /><br />
  <%= label_tag(:txtEmail, "Email", class:"lblEmail") %>
  <%= text_field_tag(:txtEmail) %>
  <br /><br />
  <%= button_tag("Send mail", class:"lblSendEmail") %>
<% end %>


See the route in the form opening tag? Yep, we'll need to define the route.

routes.rb
Rails.application.routes.draw do
    get "about/" => "about#index"
    get "langs/index" => "langs#index"
    get "langs/index/:lang" => "langs#index"
    post "sendmail/" => "sendmail#index"

    root "about#index"
end


Then add the Model. Nothing goes in here.

sendmail.rb
class Sendmail

end


And the Controller. Here, we assign the values of variables name and email to the values of the input textboxes.

sendmail_controller.rb
class SendmailController < ApplicationController
      def index
          @email = params[:txtEmail]
          @name = params[:txtName]
      end
end


Build the sendmail View using span tags and the variables. Note that the class names of the span tags have been included - lblMailSentTo and lblMailAt.

sendmail\index.html.erb
<span class="lblMailSentTo"></span> <%=@name%> <span class="lblMailAt"></span> <%=@email%>.


Let's add more stuff in your JavaScript to display the message in the correct language.

assets/javascripts/application.js
    var labels =
    [
        {
            "name" : "lblTitle",
            "content" :
            [
                {
                    "lang" : "en",
                    "val" : "Happy Easter 2018!"
                },   
                {
                    "lang" : "cn",
                    "val" : "2018复活节快乐!"
                }
            ]           
        },
        {
            "name" : "lblName",
            "content" :
            [
                {
                    "lang" : "en",
                    "val" : "Name"
                },
                {
                    "lang" : "cn",
                    "val" : "名称"
                }
            ]
        },
        {
            "name" : "lblEmail",
            "content" :
            [
                {
                    "lang" : "en",
                    "val" : "Email"
                },
                {
                    "lang" : "cn",
                    "val" : "电子邮件"
                }
            ]
        },
        {
            "name" : "lblSendEmail",
            "content" :
            [
                {
                    "lang" : "en",
                    "val" : "Send mail"
                },
                {
                    "lang" : "cn",
                    "val" : "发送邮件"
                }
            ]
        },
        {
            "name" : "lblAbout",
            "content" :
            [
                {
                    "lang" : "en",
                    "val" : "Easter is the celebration of the resurrection of Jesus from the tomb on the third day after his cruxifixion. Learn more about the meaning of Easter including the history and holiday symbols like easter eggs, the bunny, and lilies."
                },
                {
                    "lang" : "cn",
                    "val" : "复活节是在耶稣受难之后第三天从坟墓复活耶稣的庆祝活动。了解更多关于复活节的含义,包括复活节彩蛋,兔子和百合花等历史和假日符号。"
                }
            ]
        },
        {
            "name" : "lblFooter",
            "content" :
            [
                {
                    "lang" : "en",
                    "val" : "&copy; 2018 Teochew Thunder "
                },
                {
                    "lang" : "cn",
                    "val" : "&copy; 潮州雷"
                }
            ]
        },
        {
            "name" : "lblMailSentTo",
            "content" :
            [
                {
                    "lang" : "en",
                    "val" : "Mail sent to"
                },
                {
                    "lang" : "cn",
                    "val" : "邮件发送"
                }
            ]
        },
        {
            "name" : "lblMailAt",
            "content" :
            [
                {
                    "lang" : "en",
                    "val" : "at email:"
                },
                {
                    "lang" : "cn",
                    "val" : ":"
                }
            ]
        }
    ]


Now click on the "Send mail" button. Looks good, doesn't it? But we're not done, because this sucker isn't sending email yet.






Add this to the sendmail Controller. This line uses the layout easter_mailer to send mail.

sendmail_controller.rb
class SendmailController < ApplicationController
      def index
          @email = params[:txtEmail]
          @name = params[:txtName]
          EasterMailer.easter_email(@name, @email).deliver
      end
end


Now define the easter_mailer View.

easter_mailer/easter_email.html.erb
<h1>
    Name:<%=name%>
    Email:<%=email%>
</h1>


Make one for a text-only version

easter_mailer\easter_email.text.erb
    Name:<%=name%>
    Email:<%=email%>


In the mailers directory of your app folder, create this file. What happens is that any emails sent out will run the easter_email() function. This function will run the Rails method mail(). You just need to pass in the destination email address and the subject. Here, we also specify the data used for both HTML and text formats.

mailers\easter_mailer.rb
class EasterMailer < ApplicationMailer
    default from: 'easter@example.com'
   
    def easter_email(name,email)
        mail(to: "teochewthunder@gmail.com", subject: name + " has requested to know more about Easter!") do |format|
              format.html {
            render locals: { name: name, email: email }
              }
              format.text {
            render locals: { name: name, email: email }
              }
        end
    end   
end


Then, in the file below, add these lines. You need to customize this. In my case, I'm using Gmail and these are my settings. (No, my password is not "xxxxxxxxxx"!) Note that this is just for development, and you may need to do more configuration in other environments.

config/environments/development.rb
  config.action_mailer.delivery_method = :smtp
  config.action_mailer.raise_delivery_errors = true
  config.action_mailer.perform_deliveries = true
  config.action_mailer.smtp_settings = {
  :address        => 'smtp.gmail.com',
  #:domain         => 'mail.google.com',
  :domain         => 'localhost:3000',
  :port           => 25,
  :user_name      => 'teochewthunder@gmail.com',
  :password       => 'xxxxxxxxxx',
  :authentication => :plain,
  :enable_starttls_auto => true
}


Now click on the send mail button! Uh-oh...



Apparently, Gmail didn't like that. You'll probably get an email like this.


Click on the "allowing access to less secure apps" link and allow. For now, anyway. You might want to reset this after the tutorial.




Now try again! Aaaand.... we've got mail!


Yes I know, this is pretty sloppy formatting. But it worked, and you should probably do better when you have the time or inclination.


Final notes

Sending email programmatically is a damn sight easier with a framework. Because it sure isn't fun doing it in raw code.

It goes without saying, of course, that you want to use a dedicated email account or forwarding address for this.

In this tutorial, I didn't cover user input formatting. Not because it's not important - it is pretty damn important - but there was no risk of SQL Injection. Although, we could have taken some steps to filter out Cross-site Scripting.

Anyway, out of scope. Hope you had fun.

Mail the odds be ever in your favor,
T___T

Tuesday 3 April 2018

Web Tutorial: Multilingual Easter Form (Part 2/3)

Now for the second part of this web tutorial - switching languages. We already learned how to do this last February, but this time it's going to be partially front-end. In fact, the AJAX portion of the code will still be used - we're just going to change the way content is being updated.

Go to the JavaScript file. Comment out the line that causes the page to reload, because we don't want to page to reload. Now test your code. Change to another language by using the drop-down list. Then refresh the page manually. Does it stay in that language? Yes? That's because we haven't removed the AJAX code that sets the cookie which determines what language the site is currently in.

assets\javascripts\application.js
function changeLang(lang)
{
    var xmlhttp = new XMLHttpRequest();
    xmlhttp.onreadystatechange = function() {
        if (this.readyState == 4 && this.status == 200) {
            //location.reload();
        }
    };
    xmlhttp.open("GET", "../../langs/index/" + lang, true);
    xmlhttp.send();
}


Instead of reloading the page, we'll set it to run the changeContent() function. And then let's define the changeContent() function. It will accept a parameter, lang.

assets\javascripts\application.js
function changeLang(lang)
{
    var xmlhttp = new XMLHttpRequest();
    xmlhttp.onreadystatechange = function() {
        if (this.readyState == 4 && this.status == 200) {
            //location.reload();
            changeContent(lang);
        }
    };
    xmlhttp.open("GET", "../../langs/index/" + lang, true);
    xmlhttp.send();
}

function changeContent(lang)
{

}


Time for some JSON! First, declare an array labels.

assets\javascripts\application.js
function changeContent(lang)
{
    var labels = [];
}


Next, fill up the array with an object. The name property of that object will be the class name of the placeholder you are going to change content for.

assets\javascripts\application.js
    var labels =
    [
        {
            "name" : "lblTitle",           
        }
    ]


Next, set the content property of the object. It will be an array with two objects.

assets\javascripts\application.js
    var labels =
    [
        {
            "name" : "lblTitle",
            "content" :
            [
                {

                },   
                {

                }
            ]           
        }
    ]


Each object will have two properties - lang and val. lang specifies the language, and val is the translated content of that language. So in this case, the object with name lblTitle has English set to "Happy Easter 2018!" and Chinese set to "2018复活节快乐!"

assets\javascripts\application.js
    var labels =
    [
        {
            "name" : "lblTitle",
            "content" :
            [
                {
                    "lang" : "en",
                    "val" : "Happy Easter 2018!"
                },   
                {
                    "lang" : "cn",
                    "val" : "2018复活节快乐!"
                }
            ]           
        }
    ]


Now, we've set the language content of one label. Let's write the code to change the content. First, use a For loop to iterate through the labels array.

assets\javascripts\application.js
    var labels =
    [
        {
            "name" : "lblTitle",
            "content" :
            [
                {
                    "lang" : "en",
                    "val" : "Happy Easter 2018!"
                },   
                {
                    "lang" : "cn",
                    "val" : "2018复活节快乐!"
                }
            ]           
        }
    ]

    for (var i = 0; i < labels.length; i++)
    {

    }


Next, define an array, langLabels, by getting all elements with the same class name as the current element's name property.

assets\javascripts\application.js
    for (var i = 0; i < labels.length; i++)
    {
        var langLabels = document.getElementsByClassName(labels[i].name);
    }


The iterate through langLabels...

assets\javascripts\application.js
    for (var i = 0; i < labels.length; i++)
    {
        var langLabels = document.getElementsByClassName(labels[i].name);

        for (var j = 0; j < langLabels.length; j++)
        {

        }
    }


And use the filter() method on the content array in that object to get only the elements in the content array whose lang property corresponds with the value of the lang parameter. Set this to the array content.

assets\javascripts\application.js
    for (var i = 0; i < labels.length; i++)
    {
        var langLabels = document.getElementsByClassName(labels[i].name);

        for (var j = 0; j < langLabels.length; j++)
        {
            var content = labels[i].content.filter
            (
                function (x)
                {
                    return x.lang == lang;
                }
            )
        }
    }


Finally, set the innerHTML property of the current element to the val property of the appropriate content element! Since, after filtering, there should be only one element left in the content array, use the first element of content!

assets\javascripts\application.js
    for (var i = 0; i < labels.length; i++)
    {
        var langLabels = document.getElementsByClassName(labels[i].name);

        for (var j = 0; j < langLabels.length; j++)
        {
            var content = labels[i].content.filter
            (
                function (x)
                {
                    return x.lang == lang;
                }
            )

            langLabels[j].innerHTML = content[0].val;
        }
    }


It's time to put this to the test.


See how the page title changes as well? That's because both the h1 tag and the page title are styled using the class name lblTitle.


Now, to add more elements to the labels array.

assets\javascripts\application.js
    var labels =
    [
        {
            "name" : "lblTitle",
            "content" :
            [
                {
                    "lang" : "en",
                    "val" : "Happy Easter 2018!"
                },   
                {
                    "lang" : "cn",
                    "val" : "2018复活节快乐!"
                }
            ]           
        },
        {
            "name" : "lblName",
            "content" :
            [
                {
                    "lang" : "en",
                    "val" : "Name"
                },
                {
                    "lang" : "cn",
                    "val" : "名称"
                }
            ]
        },
        {
            "name" : "lblEmail",
            "content" :
            [
                {
                    "lang" : "en",
                    "val" : "Email"
                },
                {
                    "lang" : "cn",
                    "val" : "电子邮件"
                }
            ]
        },
        {
            "name" : "lblSendEmail",
            "content" :
            [
                {
                    "lang" : "en",
                    "val" : "Send mail"
                },
                {
                    "lang" : "cn",
                    "val" : "发送邮件"
                }
            ]
        }
    ]


More testing!




Let's add more content. Go to your about View and add a div with the class lblAbout, and a nice hr tag.

about\index.erb
<div class="lblAbout">

</div>

<hr />

<%= label_tag(:txtName, "Name", class:"lblName") %>
<%= text_field_tag(:txtName) %>
<br /><br />
<%= label_tag(:txtEmail, "Email", class:"lblEmail") %>
<%= text_field_tag(:txtEmail) %>
<br /><br />
<%= button_tag("Send mail", class:"lblSendEmail") %>


Then add the element for it. While we're at it, let's add one for lblFooter!

assets\javascripts\application.js
    var labels =
    [
        {
            "name" : "lblTitle",
            "content" :
            [
                {
                    "lang" : "en",
                    "val" : "Happy Easter 2018!"
                },   
                {
                    "lang" : "cn",
                    "val" : "2018复活节快乐!"
                }
            ]           
        },
        {
            "name" : "lblName",
            "content" :
            [
                {
                    "lang" : "en",
                    "val" : "Name"
                },
                {
                    "lang" : "cn",
                    "val" : "名称"
                }
            ]
        },
        {
            "name" : "lblEmail",
            "content" :
            [
                {
                    "lang" : "en",
                    "val" : "Email"
                },
                {
                    "lang" : "cn",
                    "val" : "电子邮件"
                }
            ]
        },
        {
            "name" : "lblSendEmail",
            "content" :
            [
                {
                    "lang" : "en",
                    "val" : "Send mail"
                },
                {
                    "lang" : "cn",
                    "val" : "发送邮件"
                }
            ]
        },
        {
            "name" : "lblAbout",
            "content" :
            [
                {
                    "lang" : "en",
                    "val" : "Easter is the celebration of the resurrection of Jesus from the tomb on the third day after his cruxifixion. Learn more about the meaning of Easter including the history and holiday symbols like easter eggs, the bunny, and lilies."
                },
                {
                    "lang" : "cn",
                    "val" : "复活节是在耶稣受难之后第三天从坟墓复活耶稣的庆祝活动。了解更多关于复活节的含义,包括复活节彩蛋,兔子和百合花等历史和假日符号。"
                }
            ]
        },
        {
            "name" : "lblFooter",
            "content" :
            [
                {
                    "lang" : "en",
                    "val" : "&copy; 2018 Teochew Thunder "
                },
                {
                    "lang" : "cn",
                    "val" : "&copy; 潮州雷"
                }
            ]
        }
    ]


OK, this isn't the prettiest layout in the world, but the point isn't prettiness - the point is to be able to change languages on the fly without reloading the page. I think we're doing really well here!




You may have noticed that when you refresh the page, a lot of content is missing. That's because the changeContent() function isn't run on page load. Here's a simple remedy for that...


views\layouts\application.html.erb
    <body onload="changeLang(document.getElementById('ddlLang').value)">
        <div class="container">
            <div class="header">
                <div class="right">
                    <span class="lang">
                      <label class="lblLang"></label>
                      <select name="ddlLang" id="ddlLang" onchange="changeLang(this.value)">
                      <% Lang.allowedVals["languages"].each do |key, value|%>
                          <option value="<%= key %>" <%= selectedLang(key) %>><%= value %></option>
                      <% end %>
                      </select>
                    </span>
                </div>
                <h1 class="lblTitle">
                   
                </h1>
                <div class="clearfix"></div>
            </div>

            <div class="content">
                <%= yield %>
                <div class="clearfix"></div>
            </div>

            <div class="lblFooter">

            </div>
        </div>
    </body>


Next

This page was set up to send email. So while we're at it, let's do some email sending via Rails...