Monaca Onsen UI Discord Chat Github Repo

Vue.JS + Vanilla = opinions?



  • @theprotocol I very much agree about relearning a language just because they wanted to make it unique and thus you have a 200 page manual. The one advantage Vue has over Riot (based on what you wrote as I have zero experience with Riot) is that Vue provides binding and repeaters but most importantly, it has no conflicts with Onsen - that I have discovered so far.

    I took a brief look at Riot and it looks pretty nice. I look forward to your report on getting it to work with Onsen! I am glad there are other anti-framework programmers out there like me! :smile: Personally, I am tired of everyone trying to reinvent the wheel and that is why I really like Onsen. Although it is a brand new wheel, so to speak, this wheel fits your car like it is supposed too and it works from the start like you would expect it too. Great stuff all around!

    Edit: Doing a bit more research, I found this information from Vue - now do take it with a grain of salt as it is their own comparison which one would assume is biased, but none the less, the last point was the most intirguing:

    Riot 2.0 provides a similar component-based development model (which is called a “tag” in Riot), with a minimal and beautifully designed API. I think Riot and Vue share a lot in design philosophies. However, despite being a bit heavier than Riot, Vue does offer some significant advantages over Riot:

    • True conditional rendering (Riot renders all if branches and simply show/hide them)
    • A far-more powerful router (Riot’s routing API is just way too minimal)
    • More mature tooling support (see webpack + vue-loader)
    • Transition effect system (Riot has none)
    • Better performance. (Riot in fact uses dirty checking rather than a virtual-dom, and thus suffers from the same performance issues as Angular.)


  • @munsterlander Choosing a framework is a really tough decision! There are definitely pros and cons to each one, and it’s a huge hassle to look into them. I actually researched Vue.js just before I very narrowly made the decision to settle on Riot. I’m sure many of us are all too familiar with the Javascript Framework selection dance. :)

    FYI true conditional rendering has been added to Riot as of 3.0.0 which is still in alpha. The performance of 3.0.0 also benchmarks pretty highly. The other points are valid though, the router is basic and there’s no transition system.

    The reason for the incompatibility with Onsen is that ons-navigator loads from an ons-template. I was trying to approach my app design as 1 page = 1 component (with sub-components inside it) for the most part, meaning some markup and a script – and ons-template essentially won’t load scripts. So I wonder if Vue.js components would have the same limitation with the component approach. I would imagine that it’s the same issue with Vue.js, you’d have to mount the Vue.js component representing the page contents in the ‘init’ event, and not before, as the ons-template must be loaded by the navigator first.

    It basically looks like this with Riot.js:

    <ons-template>
    <ons-page>
    <custom-riot-tag></custom-riot-tag>
    </ons-page>
    </ons-template>
    

    …and I quickly discovered I can’t immediately call riot.mount(‘custom-riot-tag’) to attach it to the DOM, because the custom tag contains a script which won’t be loaded when the ons-template gets loaded by the navigator. I’m currently calling it in the document ‘init’ event as recommended by Onsen experts (actually I learned about this from one of your responses on Stackoverflow), with a giant select statement that determines which page is being loaded, and that’s where I attach the component to the DOM. But it’s very flaky in terms of animation when I do this, for some reason.

    Do you think Vue.js would be different in this regard if one were to use the Vue.js ‘component’ approach?



  • @theprotocol Ah, I see what you are encountering now. Yes, you would have to use init or my preferred is the show event. I wrote a function that on every page show checks to see if the the DOM element exists and if so run the appropriate functions that are in an associated array in the global scope. Kind of like an Angular controller except much much simpler.

    So I create my functions, register them to the element in an array, on the show event check and see if the element exists and then execute the necessary functions. It has worked quite well for me and have only found 1 or 2 cases where I had to do something a bit different.



  • @munsterlander Yep! The problem with mounting/unmounting elements to the DOM during the ‘init’ event is that very strange things happen to the transition animation, but only on mobile (iOS) with a Cordova webview, and not on desktop Chrome / Ripple. It’s very strange and has something to do with Toolbars, as moving toolbars out of the custom component resolves a huge part of the animation problem (but it’s still not good enough and looks wrong).

    I’m also experiencing some double-mount issues which are only happening on iOS and not on desktop. A Riot component auto-mounts any sub-components. I placed some sub-components in ons-template, and on desktop they don’t get automatically mounted anymore (desired behavior, due to ons-template), but they do on iOS - so I’m wondering now if this is a Riot bug, or a quirk of ons-template. :sweat:

    Could you tell me why you prefer show over init? Thanks. :)

    PS- I guess any component-based framework that combines markup with script will have this issue, as ons-template will just remove the script half of the component.



  • @theprotocol Well, in my experience due to the lifecycle of events with show being after init, that split second delay in firing solved a lot of occasional null pointer issues. It also allows for my vanilla bindings to fire.

    As show fires every time it is displayed vs init which only fires once - although I had some weird issues where they would fire twice - I am able to do much more with dynamic forms and dynamic adding of elements and such. Luckily, Javascript by default ignores a create event if the event already exists.

    I guess in short as I don’t think I am explaining myself very well, I just found the performance of show to work better than init. Does that make sense and also your mileage may vary?

    EDIT:

    I use this a lot as I can then only fire events when those elements are shown thus giving me more granular control over the logic and processing of the data:

    document.addEventListener("show", function(event){
        if(event.target.id=='pageID') {
    etc....
    


  • @munsterlander Alright, thanks for the explanation. I’ll definitely give ‘show’ a try for mounting DOM elements. This is pretty interesting, as whatever solution ends up working will probably be the same for any component-based framework.



  • I’ve found the solution at last. I was just about to rip my hair out when it occurred to me what the solution could be:

    1. Attach (a.k.a. mount) Riot/Vue/whatever component to the incoming page during the ‘init’ event (it’s prudent to keep track in an array of what tags are mounted so you don’t double-mount them in the case of multiple init fires for the same page, which can occur in some circumstances)

    2. Instead of navigator.pushPage, use:

    navigator.insertPage(0, 'page.html');
    navigator.bringPageTop('page.html');
    

    …to sort of fake double buffering. I’ve got no more animation issues on iOS as of now, and hopefully it’ll stay that way.

    Edit: the ‘init’ event fires too many times already when using nested tabviews, and with the above change, it fires even more frequently, making it hard to attach the components. Should be fixable though.



  • @theprotocol Awesome! I agree on the init firing way too many times. That was the reason I went to the show event. I would assume init would only fire once but it seems to always be firing for some reason.


  • Onsen UI

    @munsterlander @theprotocol init event should fire only once per page. Otherwise it’s a bug :sweat_smile:
    Any short way to reproduce that behavior?



  • @Fran-Diox I will try to come up with a test. It just seemed like with the tabbar it would fire all the inits at once for all the tabs on startup and then all of them again when you would switch tabs. It may have been a coding issue on my side though.

    I will need to get more specific and see what I can come up with.


  • Onsen UI

    @munsterlander Oh! Well, now all the tabs are persistent so it’s normal that they throw init events at the beginning, but they should not throw them again after initialization.



  • Sorry for the delay in responding, I got swamped with some work. :sweat:

    In my case, the init event was firing many times because I’m using navigator.insertPage and navigator.bringPageTop to simulate double-buffering. I tried chaining them like insertPage(...).then(function(){bringPageTop()}) to avoid concurrent inits but it didn’t work and the same pages would init multiple times.

    I agree with @munsterlander though that sometimes init would fire multiple times even when using the regular pushPage too, but I can’t quite determine what circumstances cause that and can’t re-produce it. There’s definitely something related to <ons-template> containing a <ons-tabbar>, and/or nested tabbars and navigators.

    In the specific case of DOM injection on init, the source seems to be a mix of <ons-template> + <ons-tabbar> inside riot.js components. The <ons-template> seems to prevent the tabbar pages from being initialized on desktop (chrome + ripple emulator), but on mobile they did initialize (although I don’t know how to recreate this)! It’s extremely hard to diagnose because the above are inside riot.js tags, which are basically components that include html+javascript which I’m injecting into the DOM at runtime. So what happens when you inject a component with <ons-template> containing <ons-tabbar> where 1 of the tab pages also contains an <ons-tabbar> at runtime, and then load this <ons-template> from a navigator or tabbar? A huge mess. :)

    Switching back to navigator.pushPage seems to resolve the issue somewhat (with some fiddling, I managed to get init to only be called once per page now) but I’m back to the animation problems (iOS only, haven’t tested Android).

    The exact nature of the animation problems is twofold:

    1. If I inject a riot.js component into the incoming page DOM on page init, the animation shows the last frame for a split second (flicker) before occurring normally.

    2. Doing the same with an <ons-toolbar> inside the component that we inject to DOM on init completely cancels the animation (resulting in a heavy flicker and instantly going to the last frame).

    In summary, pretty much any DOM manipulation of the incoming page in page init ruins animations severely on mobile.

    I tried a gazillion workarounds, e.g. setting the page visibility to hidden for 20ms in the init event to prevent the last-frame appearing at animation start -> entire animation canceled with a flicker like in point #2 above.

    When I get some time I might try creating a plunkr which shows the animation issues, assuming I can recreate them.



  • Here is a plunkr which shows the worst case scenario of the animation problem (#2 in my last post - the transition animation is canceled when injecting an ons-toolbar with ons-back-button into the incoming page), which occurs on iOS Mobile Safari but not on desktop Chrome. In my Cordova app, it’s even worse than in this example (heavier flickering when the animation fails to occur), possibly because I have more elements on the pages.

    I couldn’t recreate animation problem #1 (last animation frame shows for a split-second upon animation start) on mobile Safari; it might only happen in Cordova, or I might be missing something about what’s causing it.

    I wonder if a different navigation approach is needed than loading a ons-template and doing DOM injection into it on ‘init’ or ‘show’. I feel like this should also be an issue with the React version of Onsen-UI… Or is the DOM injection into the ons-template done differently there? One thing is certain: DOM injection is how every framework is doing things nowadays.

    Ideally, I’d love for navigator.pushPage to expose some kind of ‘page setup’ function, specifically for injecting into the DOM of the incoming page, rather than doing everything in ‘init’, something like navigator.pushPage(page, options, callback, pageSetup).



  • I agree with @theprotocol. It definitely involved ons-template and ons-tabbar. Quick question though and maybe an observation, when you have a multi-tab tabbar, are all the tab inits supposed to fire on the first initial page load? If that is the expected behavior then ok, but it seems odd to do it when the items haven’t been attached to the DOM (or maybe they are supposed to be and there is a different issue at hand.)

    This is why I went to show. In that event, I would create additional event listeners for data binding. I would consistently get null pointer errors because the item was not attached to the DOM; however, the init event had fired. I in turn went to the show event which resolved my issue. I have yet to test everything with the new implementation of show, but I doubt it will affect my code much.



  • @munsterlander I believe ons-tabbar is persistent by default in 2.0, so all of the ons-tab do get initialized before they’re shown.


  • Onsen UI

    @theprotocol said:

    Ideally, I’d love for navigator.pushPage to expose some kind of ‘page setup’ function, specifically for injecting into the DOM of the incoming page, rather than doing everything in ‘init’, something like navigator.pushPage(page, options, callback, pageSetup).

    Yes, I actually wanted to mention it. We already planned to add some love around ons-template so we will probably implement something like that. This way you can inject custom behavior and also modify the template itself for the new page before it’s actually created. It would allow to write real templates (using handlebars or anything) in ons-template. In my mind it was something like this:

    myNavigator.pushPage('page.html', {
      templating: function(name, template) {
        return template.replace('{{placeholder}}', 'content');
      }
    });
    

    However, this may not be included in RC yet since we are focusing in bug fixes rather than new features, so probably after the release.
    Do you have any suggestions about it?

    Apart from that, we also have a rewritables object in every navigation component to inject some functionality before showing the page. This is used internally for, for example, creating page’s $scope in Angular 1 bindings. I made an example to integrate Knockout.js with beta.5 using this, so it may be helpful: http://codepen.io/frankdiox/pen/RrrXbV

    @munsterlander In v2 the tabbar attaches all the pages at the beginning so it’s normal to see 3 or 4 init events at the same time (1 per page). It could be possible that, even though the page is attached to the DOM, its inner components are not. If you find this kind of issue, please report it and we’ll check it. It’s a bit hard to control all of this because everything is asynchronous.



  • @Fran-Diox I like the proposal for templating with options but agree that it may not be a priority for everyone. With v2 going persistent - which I had implemented awhile ago based on another one of our many conversations - it definitely solved some of my issues. The problem I had with the internal components not being attached was the inability to do data binding on controls that were not visible yet. Again, show resolved that, so I don’t think it is that big of an issue. I think its more of the fact that I need to wrap my head around init better.

    Overall, I have no issues with the current RC and it is more of changing my hacking code to proper code now.



  • @Fran-Diox I understand that it’s not going to happen right away, but I’m glad to hear your plans for improved templating. This is really critical these days since most frameworks are going with the component approach, which sadly isn’t compatible with the typical innerHTML loading mechanisms of navigators and such.

    That rewritables code looks very promising. I’ll give it a shot soon, thanks! :thumbsup:



  • @Fran-Diox FYI I’m still experiencing the same animation issue whether using rewritables or init to do the DOM attachment. You can see the bug in action if you access this on iOS, where you’ll see that the animation is completely broken because the element being attached (page-2-component.html) contains an ons-toolbar.

    I understand it might not be considered urgent, although it does seem like a bug, since I can also reproduce the animation issue by doing other kinds of processing upon page init, not necessarily just injecting a riot tag into DOM.

    edit: Tested on Android and there was no animation bug, so it’s only on iOS.