Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Split HTML in different files only for development? #2525

Open
alvarotrigo opened this issue Apr 27, 2020 · 7 comments
Open

Split HTML in different files only for development? #2525

alvarotrigo opened this issue Apr 27, 2020 · 7 comments

Comments

@alvarotrigo
Copy link

alvarotrigo commented Apr 27, 2020

I find myself having to deal with a big amount of HTML and I would like to make things easier for development by splitting the code in different files.

I'm doing this with JS but I didn't find a proper way of doing it with HTML templates.

I did this in the past, but I did it by using components that load dynamically using require.js when the page gets loaded, making an extra server call for each html template we want to have in a different file:

<div class="template-content" data-bind="
    component: {
        name: 'icons'
    }
    ">
</div>
  ko.components.register(element, {
        viewModel: { instance: mm.icons },
        template: { require: 'text!views/icons.html' },
    });

Is there a way to kind of "compile" knockout.js before deploying so we can deal with different files only on development and then save ajax calls on production?

@miellaby
Copy link
Contributor

miellaby commented Apr 28, 2020

requireJS is associated with an "optimizer" https://requirejs.org/docs/optimization.html

it's a command line tool and/or an extension for various project manager like grunt or npm.

Unfortunately, it can't detect the dependency you put in data-bind directives. So you have to add these dependencies (like 'text!views/icons.html') in one of your AMD (require) modules define() list.

So when you run the optimizer on your main module, you should get a single js file with the templates defined in the form of text strings within the concatened single Javascript file.

The built app is very quick to load and doesn't need any form of invasive programming. The code works exactly the same in dev and in production.

@alvarotrigo
Copy link
Author

Unfortunately, it can't detect the dependency you put in data-bind directives. So you have to add these dependencies (like 'text!views/icons.html') in one of your AMD (require) modules define() list.

??
I'm not asking for advice on how to use require.js.
I'm just saying that's the code I used in the past. It works fine but loads HTML files dynamically making a new HTTP request to the server.

I'm asking if there's a way to structure a the app so we can have separate HTML files for different elements. Kind of the same concept of other frameworks such as Vue or React.
They just compile the code and are able to insert those files into the app without making extra HTTP request dynamically. I assume they load them all at once.

@miellaby
Copy link
Contributor

miellaby commented Apr 29, 2020

I'm sorry if I couldn't help you. English is not my native language. However I answered your exact question by providing the link to requirejs optimizer:

  • use the component binding to separate your app into multiple components. The model part is actually optional, you may just define components as pure templates to simply cut your html into parts
  • As you noticed, during development phase, requirejs loads html templates and models module on the fly.
  • then, when you'll be ready to build a production ready app, run your prefered building tool which will leverage the requirejs optimizer. The requirejs optimizer will gather all the html fragments you wrote as well as the javascript modules you developed and will make a single JS file with embedded html.
  • When the production app runs, only one file is loaded (the big bad js file) in addition to the main html, and all the components are magically started-up based on the embedded html.

@alvarotrigo
Copy link
Author

  • then, when you'll be ready to build a production ready app, run your prefered building tool which will leverage the requirejs optimizer. The requirejs optimizer will gather all the html fragments you wrote as well as the javascript modules you developed and will make a single JS file with embedded html.

How can we do that?
Once we get rid of require, we would have to modify our component registering method?

ko.components.register(element, {
        viewModel: { instance: mm.icons },
        template: { require: 'text!views/icons.html' },
    });

Any project where I can see how to implement this change?
Right now the HTML components I want to load are far less in Kb than the require.js code itself.

@avickers
Copy link

HTTP/2 allows for full duplex communication, and has nearly universal browser support at this point, so step one is making sure that your server has it enabled and is serving https only. Lots of small files can now actually be faster, as long as they are initiated within the window and, of course, single origin.

Next, you can use require or import syntax with a bundler, such as parcel or webpack. They'll create the bundles and handle transpilation via babel for you. Parcel is pretty much zero configuration. I'd try it first.

If you don't need to support IE, then there is the custom element API. You can use template literals for the HTML. Just bear in mind that you cannot applyBindings() to a shadowRoot. It's not necessary to use the ShadowDOM; however, if you do want to use it with KO, then you will need to use a wrapper.

There is also a project that is something of a KO/Vue hybrid. It seems like the author needs to quit being lazy and document it properly and tighten up their specs.

@julientype
Copy link

julientype commented Jun 7, 2020

  ko.components.register(element, {
        viewModel: { instance: mm.icons },
        template: { require: 'text!views/icons.html' },
    });

i think this a bit of over kill ...... it should be simple like DOM

myButton.addEventListener
myButton.removeEventListener

ko.applyBindings(DataPI.TablesView  , element);
ko.removeBindings(DataPI.TablesView  , element);

 iframe.onload = function() {
       try {
     ko.applyBindings(DataPI.TablesView  ,  iframe.contentWindow.body);   
       } catch (error) { 
       }   
      };
iframe.beforeunload= function() {
       try {
        ko.Remove.Bindings(DataPI.TablesView  ,  iframe.contentWindow.body);   
       } catch (error) { 
       }   
      };
      iframeCreateTable.src = "./AppendField.html";

Under the current system it registers all your templates in the same file .......

let css do its job by boot injecting it into the dom not ko .....

 self.removeTable = function(data,event) {
            DataPI.DBconnector.deleteDbTable(self.Tables,data);
            //// i sent the data object out to be processed and any changes to object reflect back...
]

ko is just static array data objects .......

var onload =  ko.computed(function() {
          ///load your arrays up.....

        }); 

they should make a min ko version to just bind json arrays around with no css and let css do what it does best. at the DOM level ko is at the JavaScript level..... its just faster this way....

var a = ko.viewmodel.getarraydata.item

wow.... i now have a binding data object to play with......
like if one view model needs to talk to the other view model......
like user model and bank model ..... you limit the amount of DOM monitoring calls this way

MyApp = loadApp();
function loadApp() {
const obj={};
obj.viewA =function(){};
obj.viewB =function(){};
obj.viewC =function(){};
obj.loadKo = ko.applyBindings(MyApp.viewA  ,documnt.body);
return obj;
}

like a view data can have many view templates....

you want to detach binding yet still have access so view get binding yet call viewB be and detaches view B needs viewC variable yet viewC needs ViewA Variable yet only viewB is binding to DOM
or have viewC and ViewB bind on the same page under 2 different div tags..

@julientype
Copy link

julientype commented Jun 7, 2020

Whenever you inject a component using the component binding or a custom element, Knockout fetches that component’s template and viewmodel using one or more component loaders.

==== think of this way your start up Javasript that will creates a variety of view models====
Like a windows registry for it software. and you can load a very large file this way....
DOM is good to 50,000 elements so iframe 50,000 more elements
this is why you need a min binding version to move json array data around
from the main app registry i send an json array view object to be process in an iframe the iframe templates loads ko mini file . i simply pass the array object to it..... css is a binding service in dom its the first object loaded in dom..... where we just need json data to bind with text attributes and values and events ......
function(data,event)
i simply use event to chance styles class name not you or styles.csstext to inject.... i use data object to play with with other object data to send it off for more processing....
your main app files object are dynamically loaded from an empty array so the app can grow and be very fast....
ko server file and ko mini you can load both or just the server file and the iframe loads the mini
load balancing memory app usage.. where the server ko binds and unbinds yet view1 can play with view2 with the server file.... let css be css let html be html and you run the binding logic service

i create a view in the object in the server i send to the ifame it gets 50,000 records did it blowup the server registry no..... the data array was created is in the mini ko file.... it may only return one item

your ko is 66kb minified now split it in 2 for client server..... and if you strip out templates and styles it gets smaller and smaller

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants