Tagged: javascript Toggle Comment Threads | Keyboard Shortcuts

  • kmitov 10:10 am on December 10, 2020 Permalink |
    Tags: javascript, ,   

    Send array params to a rails server from a JavaScript code – URL and URLSearchParams 

    The goal was to filter building instructions on buildin3d.com by brand. We had to parse and create the URL on the client so I played around with URL and URLSearchParams (again). I will try to summarize the implementation here in the hope that you could help understand how URL and URLSearchParams work and how to use them with a Ruby on Rails app.

    Rails Server side request with array param

    The server accepts a request in the form:

    https://platform.buildin3d.com/instructions?in_categories[]=1&in_categories[]=2&in_categories[]=13

    This means we could pass an array with the ids of the categories. The result will return only the 3D assembly instructions that are for Brands in these categories.

    How to send the request on the client side

    On the client side we have an <ul> element with some <li> elements representing the categories.

    The brands filter is on the left.

    When we click on the brand we would like to add the brand to the URL and redirect the user to the new URL.

    So if the current url is

    https://platform.buildin3d.com/instructions?in_categories[]=1&in_categories[]=2

    and we select a new brand I would like to send the user to

    https://platform.buildin3d.com/instructions?in_categories[]=1&in_categories[]=2&in_categories[]=13

    How to add array params to the URL with JavaScript

    Here is the whole code of the Stimulus JS controller

    import { Controller } from "stimulus";
    
    /**
     * This is a controller used for filtering by brands [categories] on the materials index page
     *
     * @author Kiril Mitov
     */
    export default class extends Controller {
      static targets = ["tree"];
    
      connect() {
        console.log("connect");
        const scope = this;
        this.setFromUrl();
        this.treeTarget.addEventListener("click", e => {
          e.preventDefault();
          const li = e.target.closest("li");
          const input = li.querySelector("input");
          input.checked = !input.checked;
          scope.goToNewLocation();
        });
      }
    
      setFromUrl() {
        const url = new URL(window.location);
        const categoryIds = new URL(window.location).searchParams.getAll("in_categories[]")
    
        Array.from(this.treeTarget.querySelectorAll("li[data-category-id]"))
          .forEach(li => {
            const input = li.querySelector("input");
            const selected = categoryIds.indexOf(li.dataset["categoryId"]) != -1
            input.checked = selected;
          });
      }
    
      goToNewLocation() {
        const url = new URL(window.location)
        const searchParams = url.searchParams;
        searchParams.delete("in_categories[]");
        Array.from(this.treeTarget.querySelectorAll("li[data-category-id]"))
          .filter(li => li.querySelector("input").checked)
          .forEach(li => searchParams.append("in_categories[]",li.dataset["categoryId"]))
        searchParams.sort();
        window.location = url.toString();
      }
    }
    

    There are a few important things in the code

    Opening the page on a new location

    window.location = url.toString()

    This will open the new page for the user

    Adding the selected brands to the url search query

    We listen for an event of click from the user and we get a list of all the checked brands.

    goToNewLocation() {
        const url = new URL(window.location)
        const searchParams = url.searchParams;
        searchParams.delete("in_categories[]");
        Array.from(this.treeTarget.querySelectorAll("li[data-category-id]"))
          .filter(li => li.querySelector("input").checked)
          .forEach(li => searchParams.append("in_categories[]",li.dataset["categoryId"]))
        searchParams.sort();
        window.location = url.toString();
      }

    First we delete the param “in_categories[]”. We use URLSearchParams.delete. This removes the param from the searchParam and if we then call .toString() we would receive the new search query without this param.

    Then we call this.treeTarget.querySelectorAll to filter all the checkboxes and then only the check once and we append “in_categories[]” param for every selected checkbox. This is what the server requires.

    searchParams.append("in_categories[]",li.dataset["categoryId"])
    

    As a result with have the query

    https://platform.buildin3d.com/instructions?in_categories[]=1&in_categories[]=2

    Because we use only URLSearchParam.append and URLSearchParam.delete all the other params are still in the search query.

    As a summary:

    We have a Ruby on Rails server that accepts an array param and we form this array param in a Stimulus JS controller. We use URLSearchParams method to append and delete params, as this will preserve the other params that are already in the url.

     
  • kmitov 9:56 pm on November 28, 2020 Permalink |
    Tags: , , javascript   

    How to do headless specs with the BABYLON JS NullEngine 

    [Everyday code]

    In buildin3d.com we are using BABYLON JS. To develop a headless specs for BABYLON JS that could run in a Node.js environment or without the need of an actual canvas we can use BABYLON.NullEngine. A spec could then look like

     const engine = new BABYLON.NullEngine();
     this.scene = new BABYLON.Scene(engine);

    Here is what I found out.

    Headless specs

    We run a lot of specs for our BABYLON JS logic. All of this specs are against preview.babylonjs.com. The preview version of babylon give us access to the latest most recent changes of babylon that are mode available to the public. These are still not release changes, but are the work of progress of the framework. They are quite stable so I guess at least the internal suite of BABYLON has passed. Preview is much like a nightly build. In BABYLON JS case it is also quite stable.

    2 days ago much of our specs failed. I reported at https://forum.babylonjs.com/t/failure-error-typeerror-cannot-read-property-trackubosinframe-of-undefined/16087. There were a lot of errors for :

    Cannot read property 'trackUbosInFrame' of undefined.

    Turns out that many of our specs were using BABYLON.Engine to construct the scene like

    const engine = new BABYLON.Engine();
    this.scene = new BABYLON.Scene(engine);

    The BABYLON.Engine class is only intended for the cases where we have a canvas. What we should have been doing for headless specs without a canvas is to use BABYLON.NullEngine. Hope you find it helpful.

    Write more specs.

     
  • kmitov 11:05 am on November 16, 2020 Permalink |
    Tags: , animations, , javascript   

    We got featured in the BABYLON JS 4.2 release video 

    About two year ago when we started working on delivering 3D instructions and playing 3D animations and visualizing 3D models in the browser. Little did we know than that with version 4.2 babylon js will feature us in their release video. Thank you BABYLON theme.

    I wrote an article on BuildIn3D.com – https://buildin3d.com/blog/buildin3d-featured-in-the-babylon-js-4-2-release-video/

    Here is the release video for all to enjoy.

     
  • kmitov 6:48 am on November 2, 2020 Permalink |
    Tags: , javascript,   

    A week ago I gave a nice lecture about Google Closure Compiler and how to use it in ADVANCED_OPTIMIZATION mode. It is available in Bulgarian at https://softuni.bg/trainings/3194/advance-javascript-compilation-with-google-closure-compiler-why-and-how

     
  • kmitov 5:54 am on October 21, 2020 Permalink |
    Tags: javascript   

    How I became one of the top JavaScript developers I know of? 

    TL; DR; How do you measure how good you are? – my highly unscientific measurement is around “when others start asking and listening to you” and when you “start delivering faster and better results”. I became really good in three steps – start solving a really hard problem, ditch the JavaScript dependencies coming from the community and pack everything.

    Context

    If you’ve been in the field of Software development and you are already a good developer and you’ve used but are generally avoiding JavaScript (because reasons), then this article is just for you. It sounds oddly specific, but in the same time I am constantly seeing many people in this situation. This was me a few years ago so I hope it could help you.

    In about 1.5 years I’ve become one of the best JavaScript developers I know of. The moment I personally started delivering better results than the much more experienced contractors I was hiring, while I was also managing our organization, I knew I have moved a long path.

    What was wrong with JavaScript?

    Nothing in particular and everything. We’ve seen countless articles from well read and published software developers over the last two decades about why JavaScript was “bad”. I am not going to get philosophical here, because I found out that they are all true and yet what stands behind the notion of JavaScript in the beginning of the third decade of the 21 century is still a very vibrant and live community achieving incredible results.

    Step one. Find a difficult real-world problem.

    My problem was to build an event-driven plug-in architecture in JavaScript while incorporating and refactoring legacy code that became one of the extensions. This was version 6 of our Instructions Steps Framework delivering the 3D assembly instructions at buildin3d.com. We are currently working about a hundred extensions.

    Finding a difficult problem could really focus you. It was not difficult from an algorithmic or mathematical point of view. It was not difficult as a new blockchain or AI or another emerging technology. It was difficult from an architectural point of view. How do you build a framework for plugins that live in a browser and that must incorporate some legacy behavior.

    I guess finding a difficult problem applies to learning all kinds of technologies.

    Step two. Ditch the dependencies. Go vanilla.

    The community is large. We have JS in the browser, in native apps, on the server. We have JS everywhere. The entry barrier is extremely low. You can build a new JS tool today, publish it on Reddit and have hundreds of people trying it tomorrow. In half a year your have hundreds of npm packages that are dependent on you, you change something and the whole npm is broken. This happens more often than you might think.

    Most of the solutions that could be found on the internet are outdated or plain wrong. Most of the solutions are not about the problem that is addressed. I guess because of the size of the community there are so many “answers and solutions” on the internet around – “take this library and do this call”, but these are not solutions for the case you are working on and are solutions that surely will not work in a year or two.

    Example. I’ve never in my live done something like:

    $ mv --version 1.2 source.md target.md

    This is a command for moving the file source.md to target.md where I’ve specified the “version” of the mv that is to be used. Does this sound familiar? No, of course. I am not sure even if it is possible to specify the version of mv to be used. I had an enormous difficulty explaining this to the maintainers of some of the popular tools used in the JS world and they did not agree with me. (No link here because I don’t want to point fingers.)

    I can not forget an interesting lecture by Linus Torvalds that I once watched where he was:

    You never break libraries ABI (Application Binary Interface). This is the thing I am very serious about in Linux Kernel. You don’t break ABI. You support it.

    But that is not how much of the JS community and in fact many other communities think.

    So I ditched the dependencies. I went full scale vanilla JS. I was not even depending on the DOM for much of the implementation and the things that were dependent on the DOM and some of the WEB APIs were extracted into proper extensions. Without any dependencies I had to do more work, and I would admit I had to write a tree traverse and observers on my own which I never expected I would have to. But at the end this ends up to be trivial implementations extracted to a proper extension that we could easily change with a new more well maintained library (when I find one).

    Step three. Pack and Go.

    Webpack it is. There are others. I’ve used many tools and I found out it is extremely important to pack your JavaScript properly and to understand the whole process. So look at the tool for packaging and try to develop the JS into different modules but pack them all into a single file that you deliver to the browser. The amount of issues you could face with this is really astonishing.

    Step four. Debug a jQuery problem.

    It took me weeks to migrate one of the platforms that we are running from jQuery 1 to jQuery 3.5. The amount of work required to completely remove jQuery dependencies was still too much so we decided to first migrate it and probably later remove it. Our code was not dependent on jQuery1, but some of our dependencies were. I have a friend that is now migrating from jQuery 3.4 to jQuery 3.5. I call these things the “jQuery problem” – as the library made it so easy to approach and solve different problems it was used in all kinds of unintended ways that it was never designed to. As the answers in blog posts and internet forms were based mostly around jQuery there was a decade or two with a lot of copy/pasting going on. Debugging such hard problems could be an eye opener.

    Conclusion

    A lot of other things matter – like choosing the right tools. But in retrospect these were the things I learned most from.

     
  • kmitov 4:22 am on October 21, 2020 Permalink |
    Tags: , javascript   

    Advanced compilation with Google Closure Compiler 

    Tonight I am doing a live stream lecture about Google Closure Compiler and how we use it to compile the JavaScript code of the Instructions Steps Framework. The framework is the core of buildin3d.com where we deliver 3D assembly instructions.

    The repo for the lecture is located at https://github.com/thebravoman/google-closure-compiler-presentation/.

     
  • kmitov 5:41 am on October 6, 2020 Permalink |
    Tags: javascript, progressive web application, pwa, , ,   

    The path to a progressive web app – or how we skipped the whole JS Frameworks on the client thing. 

    I recently responded to a question in the Stimulus JS forum that prompted me to write this blog.

    About 6 months ago we decided to skip the whole “JSON response from server and JS framework on the client” stuff and we’ve never felt better. We significantly reduce the code base while delivering more features. We manage to do it with much less resources. Here is an outline of our path

    Progressive Web Application

    We had a few problems.

    1. Specs that were way to fragile and user experience that was way to fragile. Fragile specs that are failing from time to time are not a problem on their own. They are an indicator that the features also do not work in the client browsers from time to time.
    2. Too much and two difficult JS on the client. Although it might see that making a JSON request from the client to the server and generating the HTML from the response might seem as a good idea, it is not always a good idea. If we are to generate the HTML from a JSON, why don’t we ask the server for the HTML and be done with it? Yes, some would say it is faster, but we found out it is basically the same for the server to render ‘{“video_src”: “https://…&#8221;}’ or to render “<video src=’https://…&#8217;></video>’ . The drawback is that in the first scenario you must generate the video tag on the client and this means more work. Like twice the amount of work.

    So we said:

    Let’s deliver the platform to a browser that has NO JS at all, and if it has, we would enhance it here and there.

    How it worked out?

    In retrospective… best decision even. Just know that there is not JS in the browser and try to deliver your features. Specs got a lot faster and better. 1h 40 m compared to 31 minutes. They are not fragile. We have very little JS. The whole platform is much faster. We user one framework less. So, I see no drawbacks.

    First we made the decision not to have a JS framework on the client and to drop this idea as a whole. For our case it was just adding complexity and one more framework. This does not happen overnight, but it could happen. So we decide that there is no JS and the whole platform should work in the case of JS disabled on the browser (this bootstrap navigation menus are a pain in the a…). It should be a progressive web application (PWA).

    After this decisions we did not replace JSON with Ajax calls. We skipped most of them entirely. Some JSON requests could not be skipped, but we changed them as AJAX – for example “generating a username”. When users register they could choose a username, but to make it easier for them we generate one by default. When generating we must make sure it is a username that does not exists in the DB. For this we need to make a request to the server and this is one place we are using Stimulus to submit the username.

    A place that we still use JSON is with Datatables- it is just so convenient. There are also a few progress bars that are making some legacy JSON requests.

    Overall we have Ajax here and there, an a few JSON requests, but that’s it. Like 90-95% of the workflow is working with JS disabled.

    We even took this to the extreme. We are testing it with browsers with JS and browsers without JS. So a delete button on a browser without JS is not opening a confirmation. But with JS enabled the delete opens a confirmation. I was afraid this will introduce a lot of logic in the specs, but I am still surprised it did not. We have one method “js_agnostic_delete” with an if statement that check if JS is enabled and decides what to do.

    My point is that moving JSON to Ajax 1:1 was not for us. It would not pay off as we would basically be doing the same, but in another format. What really payed off and allowed us to reduce the code base with like 30-40%, increase the speed and make the specs not so fragile was to say – “let’s deliver our platform to a JS disabled browser, and if it has JS, than great.”

    To give you even more context this was a set of decisions we made in April 2020 after years of getting tired with JS on client. We are also quite experience with JS as we’ve build a pretty large framework for 3D that is running entirely in browser so it was not like a lack of knowledge and experience with JS on our side that brought us to these decisions. I think whole team grew up enough to finally do without JS.

     
  • kmitov 4:40 am on April 2, 2020 Permalink |
    Tags: , javascript, promise   

    Should you care about the settlement of Promise(s) or use Promise.finally() – [Everyday code] 

    This article is part of the series – [Everyday code]

    – This logic should not be in Promise#finally()?

    – Why? We just care that the Promise is settled.

    – No. We care why it is settled.

    TL; DR;

    You might be tempted to put some specs logic in Promise#finally(), but here is why you should not do it.

    It’s like try/catch/finally

    In Instruction Steps Framework we try to load the list of parts in the instructions. There could be no list of parts in the instructions. How should we test this?

    Consider the examples:

    it("shows message 'No parts list provided' when there is no parts list", function() {
          // get the promise that the part list will be loaded, 
          // but we know that it will not be loaded, 
          // because this is how we setup the test. 
          this.promise = ... 
    
          // Using then()
          this.promise.then(() => {
                  expect($("#partsList").text()).toContain("No parts list provided");
                  done();
                });
    
          // Using catch()
          this.promise.catch(() => {
                  expect($("#partsList").text()).toContain("No parts list provided");
                  done();
                });
    
          // Using finally()
          this.promise.finally(() => {
                  expect($("#partsList").text()).toContain("No parts list provided");
                  done();
                });
        })
        

    Would you use then(), catch() or finally() in the spec?

    Using then()

    The purpose of the promise is to load a file. The file is not there. So it is not successfully settled. As this is not successfully settled then() should not be called.

    Using catch()

    The promise is promising us that it will load a file and show something on the screen. It fails. It settles, but fails. It would be best to put the spec in catch().

    Using finally()

    The promise fails as the test is setup like this. We’ve setup the test to have the wrong url. But this is only in this test. What if other clients are waiting on the same promise in a production code. Should they use finally()?

    From the finally documentation – https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/finally

    The finally() method can be useful if you want to do some processing or cleanup once the promise is settled, regardless of its outcome.

    The key here is “regardless of its outcome”. We get a Promise that is promising us to load a file. It fails. We care about the outcome. We care to have a successfully loaded list of parts, and if there is an ‘exceptional case’ we should catch() it and process it. We know exactly why the promise settles. We care.

     
c
compose new post
j
next post/next comment
k
previous post/previous comment
r
reply
e
edit
o
show/hide comments
t
go to top
l
go to login
h
show/hide help
shift + esc
cancel