Good way to integrate query-layout with reactive templates + async data sources? - facebook-javascript-sdk

I'm working on a Meteor app that gets data from Facebook & I would like to use jquery-layout for presentation. I suspected that there might be some "subtleties" when trying to use jquery to modify HTML in reactive templates, so I set up a relatively simple test case that goes something like this (paraphrased for brevity)...
<body>
{{> mainTemplate}}
</body>
<template name="mainTemplate">
{{#with userInfo}}
{{> partialNorth}}
{{> partialWest}}
{{> partialCenter}}
{{> partialEast}}
{{/with}}
{{layItOut}}
</template>
Template.mainTemplate.userInfo returns contents of a Session variable that starts with a default value and asynchronously get updated with info from Facebook.
Template.mainTemplate.layItOut sets up a call to Meteor.defer with a callback fcn that actually executes the 5 lines of jquery-layout code.
And that seems to work pretty well...
the initial display is as expected/intended (although there's a brief period where the page is not laid out)
any updates to the reactive context cause re-execution of the layout (again, w/brief-but-visible re-layout)
So, why am I whining? Mostly I would like to find a cleaner approach that does away with the noticeable re-layout activity.
I could make the reactive contexts more granular, but I'm not sure that this would really help.
Alternatively, I suppose I could experiment with directly controlling rendering (e.g., via Meteor.ui.render() , but that sounds like a lot of work ;-)
I think what I'd really like is either
a) a way to hook into Meteor render events
or better still
b) a cleaner way to connect query-layout to templates
Thoughts?

So I managed to educate myself enough to answer my own question in case anyone else finds it useful. Bottom line is that I was wrong on several levels; making the reactive contexts more granular is the answer (or at least an answer).
In the example I gave, I made the whole page reactive by placing all of the rendering within the #each construct. What I now do is try to make the reactive contexts as small as possible so that only a (relatively) small part of the page is re-rendered on any reactive change and all of the reactive elements are contained below (or way below) the level of the jquery ui elements.
I.e., something like this:
<body>
{{> mainTemplate}}
</body>
<template name="mainTemplate">
{{#with userInfo}}
{{> partialNorth}}
{{> partialWest}}
...
{{/with}}
{{layItOut}}
</template>
<template name="partialNorth">
<div class="ui-layout-north"> <-- definition for jquery-layout north panel
<h1>This is the north pane</h1>
<p>The user data go next:</p><br />
{{> templateUserData}}
</div>
</template>
<template name="templateUserData">
<div>
{{#with theUserData}} <-- Assumes a helper function 'theUserData' for this template
<p>First name: {{first_name}}</p>
<p>Last name: {{last_name}}</p>
...
{{/with}}
</div>
</template>
Once you have the reactive elements below the jquery ui elements (I have tried it with panels, tabs, accordions, buttons and pop-ups so far) the whole thing works just like it said it would in the shiny brochure! ;-)

Related

Vue: communicating parameters from app to component and back, using computed properties

Sorry for the beginner question (I'm fairly sure this will be a duplicate, but I actually can't figure out what terms to use in order to find it). I just started with Vue.
I am just getting started with Vue, and following this course (https://www.vuemastery.com/courses/intro-to-vue-js/communicating-events). In this problem, there is (and here I don't know the term, so I'm going to go with...) app-level data parameter called cart. cart is an array which holds the id of each item a user has added to cart.
The problem tells us to add a button to remove items from the cart.
I ran into problems trying to create a computed property, which would allow me to hide the "remove" button in the event the selected item is not in the cart (eg. `
Communicating data from the app-level (the cart array), to the component-level (to a computed property in the product component), so that I could use something like :hidden="!inCart" on the "Remove from Cart" button, which is itself defined in the component. inCart would be a computed value here.
Communicating the selected product from the component to the app-level, computing inCart at the app level, then using the computed value at the component-level.
Either way, I can't seem to figure out how to do this in the way I would want to, which would look something like how v-bind operates. Namely, I think I may be able to hack together a solution using methods (which I believe have to be triggered by certain events), but I don't understand how I might go about this using built-in functionality such that the value of inCart is dynamically auto-computed.
Maybe there would be an answer to this in the next few courses, but I don't see us covering that in the intro material. Sorry for the neophyte question. Thank you in advance.
In Vue the way you communicate "state" from higher-level objects to lower-level objects is through props.
So, assuming your app looks something like...
<MyApp>
<MyShoppingPageWithItems>
<MyItem></MyItem>
<MyItem></MyItem>
<MyShoppingPageWithItems>
</MyApp>
You need to pass the cart object down as a prop.
So in your MyShoppingPageWithItems template, you'll have something like...
<template>
<div>
<MyItem v-for="item in items" :item=item :cart="cart"?
</MyItem>
</div>
</template>
And in your item template...
<template>
<div>
<div>
{{item.name}}
</div>
<div v-if="cart.includes(item.id)">
Remove button or whatever
</div>
</div>
</template>
Not that the .includes() method is a native JavaScript method, which you can read more about here.
Edit
To reference a prop in a computed property (or anywhere else in a Vue component), just refer to this.propName, as demonstrated here in the Vue docs.
So, if you want to create a computed property, you can do the following:
<template>
<div>
<div>
{{item.name}}
</div>
<div v-if="isInCart">
Remove button or whatever
</div>
</div>
</template>
<script>
export default {
props: ['cart', 'item'],
computed: {
isInCart() {
return this.cart.includes(this.item.id)
}
}
}
</script>
Note that the formula is the exact same as above, but just includes this. for cart and item. In templates, the this. is implied when referring to props, data, and computed properties.

Why are Vue dynamic components destroyed inside loop every re-render?

I run into an issue with Vuejs 2.x version (latest). While rendering a list of item inside a loop, if I make changes to the items then the normal components are not destroyed but the dynamic components will always be destroyed:
I have put a short sample code here:
https://gist.github.com/yellow1912/fc1c053e07c1ca136148484cf7f79d1a
I have also put a codepen here:
https://codepen.io/raineng/pen/zYGOXYY?editors=1111
<nl-test inline-template>
<div>
<div v-on:click="increase"> increase here please </div><br><br>
<div v-on:click="decrease"> decrease here please </div>
<ul>
<li v-for="(value, key) in getItems()" :key="key">
printing
<component :is="getItem()" :key="key"></component>
<nl-test inline-template>
<div>
this is a test here
</div>
</nl-test>
</li>
</ul>
</div>
</nl-test>
To see what I mean, open the console tab on codepen, click the add item and you will see that the dynamic component items are destroyed and re-created everytime.
I found out why, I need to use keep-alive:
https://v2.vuejs.org/v2/guide/components-dynamic-async.html
To quote:
When switching between these components though, you’ll sometimes want
to maintain their state or avoid re-rendering for performance reasons
Recreating dynamic components is normally useful behavior, but in this
case, we’d really like those tab component instances to be cached once
they’re created for the first time. To solve this problem, we can wrap
our dynamic component with a element
Wasted 2 days on this issue and then I found the answer just a moment after posting this on StackOverflow. Hope it helps someone.

Aurelia: How can I modify sidebar content from inside a router view?

I'm trying to wrap my head around how "inner components" can adjust the content of "outer components". Let's say I have an application template that looks something like this:
<template>
<div class="sidebar">
<div>Some app-wide content</div>
<div>
<!-- I want to put some view-specific content here -->
</div>
</div>
<div class="main-body">
<router-view></router-view>
</div>
</template>
Each subview wants to render different content to the sidebar. Obviously this would be easy if the subview included the sidebar area itself, but let's say it is important to preserve the structure and we don't want to replicate the boilerplate of the sidebar across every view.
Is there any way for a child view to declare "export this extra component for display in another place?" I imagine something like injecting the parent view and calling a method on it, but I can't figure it out from the documentation.
Simple demo:
It's fairly simple, actually. Just import and inject your sidebar or any other viewmodel and call a method or update a property.
https://gist.run/?id=745b6792a07d92cbe7e9937020063260
Solution with Compose:
If you wanted to get more elaborate, you could set a compose view.bind variable to that your sidebar would pull in a different view/viewmodel based on the set value.
https://gist.run/?id=ac765dde74a30e009f4aba0f1acadcc5
Alternate approach:
If you don't want to import, you could also use the eventAggregator to publish an event from the router view and subscribe to listen to that event from your sidebar and act accordingly. This would work well for a large app setting where you didn't want to tie them too closely together but wanted the sidebar to react correctly to unpredictable routing patterns by always responding when triggers were published.
https://gist.run/?id=28447bcb4b0c67cff472aae397fd66c0
#LStarkey's <compose> solution is what I was looking for, but to help others I think it's worth mentioning two other viable solutions that were suggested to me in other forums:
View ports. You can specify multiple named router views in a template and then populate them by passing in a viewPorts object to the router instead of specifying a single moduleId. The best source of documentation is a brief blurb in the "Cheat Sheet" of the Aurelia docs.
Custom elements. It's a little more "inside-out" but you could define most of the outer content as a custom element that has slots for the sidebar and the main body; each child view would define this custom element and pass in the appropriate pieces.

Vue-js and different layouts

how would one layout the router-views, if you have, lets say 3 different layout to use for you app (e.g. layout for customers, layout for employer and admin-interface).
At the moment i implemented the customer "view" like:
<template>
<div id="app">
<HeaderBar/>
<Navigation></Navigation>
<router-view></router-view>
</div>
</template>
inside the App.vue file. I could use something like:
<div id="app">
<router-view name="header">
<HeaderBar/>
<Navigation></Navigation>
</router-view>
<router-view></router-view>
</div>
</template>
and load different headers for those "subroutes" but this seem's to be odd.
Also what, if i'd like to use an other index.html?
I'm using webpack for this app.
Or would you suggest to create different apps for this?
Many thanks
rene
You could try using dynamic components.
Basically you change which component is being rendered depending on your route.
So it would probably be something like this:
<component v-bind:is="headerComponent"></component>
and then your app can contain heeaderComponent property in data object that has a default value and gets changed when you click on a route that should use different header. Rinse and repeat for the footer.
As for the last question, I think you should only use one app. As it is possible to do and everything is still connected. I'm not sure but I don't know how would you, if need be, communicate between different instances of Vue.

Aurelia: if binding and content selectors

I have found content selectors don't seem to work when an if binding is used on a parent element. For example:
some-element.html
<template>
<div if.bind="true">
This will appear
<content></content>
</div>
</template>
app.html
<template>
<some-element>This will not appear</some-element>
</template>
This works fine if I don't use the if binding, but will not render <content> when I do use the if binding. Is there something I'm doing wrong here or is there a way to get this to work as expected?
I'll be adding this to our documentation soon, but for now, here is #EisenbergEffect's explanation from https://github.com/aurelia/framework/issues/375
"No. That is a characteristic of the shadow dom. The content selection points have to be static. They cannot be added ore removed dynamically. If you want to hid them, then consider using show.bind instead."