Backbone Marionette extra div wrappers: tagName solution in other threads not working for DYNAMIC tabs - dynamic

Below is a snippet from a sample tab control from here: http://layout.jquery-dev.net/demos/tabs.html
Those tabs inside those splitter panes are exactly what I need. But I need the tabs to be dynamically added and removed based on user interaction. Thought I could do it with composite or collection views in backbone marionette - add/remove things from the underlying collection and tabs appear and disappear in the UI.
Conceptually this shouldn't hard. But the difficulty comes in with the the dreaded extra wrapping div problem that is so discussed in various threads over the years.
Extra divs in itemviews and layouts in Backbone.Marionette
In my case, each li in my ul comes up wrapped in a it's own div. That messes up the CSS in the code snippet below. It breaks some of it, so things look weird. If I could just produce the output below, things wouldn't look weird.
<ul class="ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all ui-sortable" role="tablist">
<li class="ui-state-default ui-corner-top ui-tabs-active ui-state-active" role="tab" tabindex="0" aria-controls="tabs-west-1" aria-labelledby="ui-id-1" aria-selected="true">
Nunc tincidunt
</li>
<li class="ui-state-default ui-corner-top" role="tab" tabindex="-1" aria-controls="tabs-west-2" aria-labelledby="ui-id-2" aria-selected="false">
Proin dolor
</li>
</ul>
The output above has complex li's rendered directly in the ul container which is also complex. Why can't I make that output happen?
This extra wrapping div seems to be the default behavior of backbone marionette when itemView's are rendered. Many answers in these threads center on setting the tagName property to li. Problem is that produces a blank, generic li. Look at the li's in the snippet above. They have lots of properties.
My li's will need to have unique id's and styling and other attributes like those above. Making your item view's tagName an li just produces a simple li in your output. How do I get my dynamic id's and styling classes and such in there?
My first attempt to fix this was to pull the li code into my handlebars template so I could encode it with the dynamic data I needed:
var TabItemView = Marionette.ItemView.extend({
template: Handlebars.compile(
'<li class="ui-state-default ui-corner-top ui-tabs-active ui-state-active" role="tab" tabindex="0" aria-controls="tabs-{{id}}" aria-labelledby="ui-id-{{id}}" aria-selected="true">' +
'{{tabName}}' +
'</li>'
),
});
But that now wraps each li in my list in it's own container div. Every combination of using el, tagName, appendHtml override produces either a wrapping element or incorrect results - like appendHtml override causes each new tab to simply replace existing tabs - so I only ever have one tab, the latest one added.
What am I missing here? Why can't I define a template for an li containing dynamic data in it and render it DIRECTLY to my ul WITHOUT an extra wrapping div around each list element? I guess another way to phrase the question is I am working with an existing template that REQUIRES complex li's. How do I produce them in a composite or collection view WITHOUT the extra wrapping container around them?
Backbone and Marionette seem to offer the promise of being able to do things like this in a way more manageable than doing it in straight jQuery. I'm not having that experience. What am I not seeing?

I don't use Marionette, but I don't see why it would override Backbone's default behavior. This should give you an idea:
var TabView = Marionette.ItemView.extend({
tagName: 'ul',
className: 'ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all ui-sortable" role="tablist',
render: function() {
//something like this
var _this = this;
this.collection.each(function(item){
var view = new TabItemView({
model: item
});
_this.$el.append(view.render().el)
})
}
});
var TabItemView = Marionette.ItemView.extend({
tagName: 'li',
className: 'ui-state-default ui-corner-top ui-tabs-active ui-state-active" role="tablist',
template: Handlebars.compile(
'{{tabName}}'
),
});

Regarding managing views and data to produce specific HTML output, I suggest you take a look at my blog post here: http://davidsulc.com/blog/2013/02/03/tutorial-nested-views-using-backbone-marionettes-compositeview/
That should get you to structure the views so they render properly. Then, you can add http://backbonejs.org/#View-attributes to the mix to get your complex attributes (for which there aren't existing Backbone attributes such as className).

Related

Is it possible to not omit some opts on the outermost element in a custom Riot tag?

I'm using RiotJS v3.9
I've written a custom tag that accepts a few opts. The problem is that the markup it generates includes all of those opts on the outermost element, in addition to the interior tags where I explicitly deposit them. I do not want any opts to appear on the top element unless I make that happen.
In this case, my custom tag display a list of items. One of the opts it accepts is the value for a specific data- attribute on each list item. So, I want data-something={opts.itemSomething} to appear on each list item, but I do not want that to appear on the wrapper.
// my-list.tag
<my-list>
<ul data-something={ opts.something }>
<li
each={ item in opts.items }
data-something={ parent.opts.itemSomething }
>
{ item }
</li>
</ul>
</my-list>
Using it:
<my-app>
<my-list
something="parent-value"
item-something="child-value"
items={['one', 'two', 'three']}
/>
</my-app>
What it emits into the page:
<my-list something="parent-value" item-something="child-value">
<ul data-something="parent-value">
<li data-something="child-value"> one </li>
<li data-something="child-value"> two </li>
<li data-something="child-value"> three </li>
</ul>
</my-list>
I don't want the emitted <my-list> tag to have either the parent-value or the child-value on it. I only want those attributes to appear on the <ul> and <li>, like I coded it.
// bad output
<my-list something="parent-value" item-something="child-value">
// good output
<my-list>
Is this possible?
Also, I know from working with React that I'm likely to encounter future cases where I want some of the opts to appear on the wrapper while hiding others. So, ideally I'd like to know how to control this behavior on a per-opt basis.
you can remove the unwanted attributes in both the "updated" and "mount" event.
check this demo
However I strongly suggest you to switch to riot#5!!

Using dynamic IDs in a string in a VueJS

I'm using a UIKit library for a tab component that listens to a uk-tab property that targets an id. The problem with this, is that it creates the same ID for every tabbed component. I like the UI, but whoever thought of this, didn't think too far into it. I could fix it by making the id dynamic but I am having trouble calling it in the uk-tab property because it is rendering a string. Coming from a react background, I would do a string literal and some JSX, something like #item-${_id}to show #item-12, #item-13....and so on. But That's not working. How can I do this in Vue?
Here is an example of how it works
<div class="mytrigger">
<ul uk-tab="connect: #component-tab-left; animation: uk-animation-fade">
</div>
<div class="mytargetedtab">
<ul id="component-tab-left" class="uk-switcher">
</div>
Here is an example of how what I need
<div class="mytrigger">
<ul uk-tab="connect: #_uid+'switcher'; animation: uk-animation-fade">
</div>
<div class="mytargetedtab">
<ul :id="_uid+'switcher'" class="uk-switcher">
</div>
Check out the dev tools. It should be 810switcher, but instead is taking it as a string
Any ideas? Thanks
I believe what you need is:
<ul :uk-tab="`connect: #${_uid}switcher; animation: uk-animation-fade`">
Or if you prefer not to use backticks:
<ul :uk-tab="'connect: #' + _uid + 'switcher; animation: uk-animation-fade'">
The output will be:
<ul uk-tab="connect: #22switcher; animation: uk-animation-fade">
A few notes:
Using a : is short for v-bind: but don't let the name confuse you. v-bind doesn't necessarily bind anything, it just makes the attribute value a JavaScript expression.
I'd avoid using numbers at the start of element ids, I've seen that cause problems in the past. It'd be better to put the numbers at the end.
The underscore at the start of _uid indicates that it's private to Vue. There are no guarantees about what form it will take or whether it will even exist going forward.
Use data-uk-tab instead of uk-tab like below.
<div class="mytrigger">
<ul data-uk-tab="{connect: `#${_uid}switcher`, animation: 'uk-animation-fade'}">
</div>
<div class="mytargetedtab">
<ul :id="_uid+'switcher'" class="uk-switcher">
</div>
For more information => Switcher with tabs
You can use any javascript expression in a data binding in vue. So, if you bind a string template to the attribute, it'll populate what you expect.
<ul :uk-tab="`connect: #${uid}switcher`'; animation: uk-animation-fade">

angular2 bootstrap4 tooltip doesn't render html, while popover does

I'm using angular2 and bootstrap4. Popover correctly renders raw html as bold text asdf
<img src="assets/images/1.jpg"
data-container="body" data-toggle="popover" data-html="true" data-placement="top"
[attr.data-content]="getM()"/>
However, tooltip renders as plain <b>asdf</b> text including tags
<img src="assets/images/2.jpg"
data-container="body" data-toggle="tooltip" data-html="true" data-placement="top"
[attr.title]="getM()"/>
Component method getM:
public getM(): string {
return '<b>asdf</b>';
}
Both tooltip and popover are initialized the same way
$(function () {
$('[data-toggle="tooltip"]').tooltip({container: 'body'});
$('[data-toggle="popover"]').popover({container: 'body'});
})
Could someone explain why is that and how to solve? It seems this is connected with initialization order, but I just don't know where to look further.
Well, the issue was that my element (which tooltip was attached to) was created dynamically.
In exact, I had a 1 sec delayed service. When new data arrived, the <img> element in my component was recreated, but $('[data-toggle="tooltip"]') selector doesn't work with dynamic elements.
Instead, I had to use this selector
$("body").tooltip({selector: '[data-toggle="tooltip"]'});
Now it works as intended.
PS I'm not a front-end developer, so anyone who can explain it in better terms is welcome.

result hits in multiple columns using Algolia and Bootstrap 3

I'm using Algolia instantsearch.js for search, and to display my result page, I'm using Bootstrap 3, and I'd like hits to be displayed in 3 columns:
qry | hit1 | hit2
| hit3 | hit4
| hit5 | hit6
(where qry = the search query input widget)
etc..
If it's mobile, it should display as:
qry
hit1
hit2
hit3
etc.
Can someone help me with the html/css I can use to implement this?
Thanks!
Basically, you want to use bootstrap rows and grid layout col-{xs,sm,md,lg}-X (More info about the grid layout here).
One interesting property with bootstrap is that if you declare a block as being col-YY-X, if the screen width is under the dimensions YY is associated with, it automatically expands to the full width.
instantsearch.js's widgets expose a cssClasses parameter that allows you to customize the classes of the underlying markup.
To easily do two columns, all you need to do is declare the root element of the cssClasses as being a .row, and each result as a .col-sm-6 (or .col-md-6 or .col-lg-6 depending on which screen size you want it to apply).
By combining them, you can have some really interesting layouts.
See this JSFiddle
Here, I've extended a bit the idea. Try to resize the view, and you'll see that it automatically picks a number of results to display per line depending on the width by combining multiple col-YY-X classes on the hit widget.
search.addWidget(
instantsearch.widgets.hits({
container: '#hits',
templates: {
empty: 'No results',
item: '<div class="hit">{{title}}</div>'
},
hitsPerPage: 6,
cssClasses: {
root: 'row',
item: 'col-lg-3 col-md-4 col-sm-6'
}
})
);
As you can see, I've also added an inner class to the item template to be able to use the item as a wrapper with padding inside to avoid having the hits glued to each other. I apply the border to the inner element, because adding margins to bootstrap grid elements is not the right solution.
The layout itself is really simple, you can just nest rows together:
<div class="container-fluid">
<div id="search" class="row">
<div class="col-sm-4">
<div id="input"></div>
</div>
<div class="col-sm-8">
<div id="hits" class="row">
</div>
</div>
</div>
</div>

Durandal: Showing a 'LOADING...' during composition

I can easily show a loading message while the activate method is doing its thing like so:
<div data-bind="compose:ActiveVm">
<div class="text-center" style="margin : 75px">
<i class="fa fa-spinner fa-spin"></i>
</div>
</div>
However if I then update my ActiveVm property with a different viewmodel, the splash content does not show. I understand that the splash content is only designed to show on 'initial' load, but what options do I have for displaying such a message when transitioning from one viewmodel to another?
Note that this composition does not participate in routing...
Update: Related durandal issue here which might be of value to future visitors: https://github.com/BlueSpire/Durandal/issues/414
This begs for a comment of 'what have you tried?' but given that I could see the benefit of this for future users I wanted to throw in my $0.02 -
The splash displays on your screen until Durandal loads up the application and replaces the div with id="applicationHost" 's content with the shell view and the subsequent views that are loaded. If you wanted to make this a re-usable component one thing that you could do is to take that Html.Partial view that is being loaded and create your own view inside of your app folder in your Durandal project.
For example you would create a new HTML view inside of your app folder -
splashpage.html
<div class="splash">
<div class="message">
My app
</div>
<i class="icon-spinner icon-2x icon-spin active"></i>
</div>
And then compose it from your shell -
<div data-bind="if: showSplash">
<!-- ko compose: 'splashpage.html' -->
<!-- /ko -->
</div>
And in your view model you would toggle the observable showSplash whenever you want to show / hide it -
var showSplash = ko.observable(false);
var shell = {
showSplash: showSplash
};
return shell;
And you could call that from your activate methods inside your other view models like this -
define(['shell'], function (shell) {
function activate() {
shell.showSplash(true);
// do something
shell.showSplash(false);
}
});
This sounds to me like a scenario where a custom transition may be useful. When the composition mechanism switches nodes in and out of the DOM, it can use a transition.
This page, under Additional Settings>Transition (about halfway down) describes a custom transition: http://durandaljs.com/documentation/Using-Composition/