I have an issue with using a linked template with JSViews and Materialize Dropdown Button.
The first time, the view is rendered, it works fine, however as soon as I observably update the status of the button, the ul tag which is linked to the button is deleted from the DOM.
I have created a test case on JSFiddle: here
<script id="test_template" type="text/x-jsrender">
{^{for tasks}}
<div class="dropdown change-state" style="display: block">
{^{if Status == 0}}
<a href="#" class="completed-state-box dropdown-button btn btn-flat red" data-constrainwidth="false" data-activates='review_status_dropdown-{{:Id}}'>NOT COMPLETE</a>
{{/if}}
{^{if Status == 1}}
<a href="#" class="completed-state-box dropdown-button btn btn-flat green" data-constrainwidth="false" data-activates='review_status_dropdown-{{:Id}}'>COMPLETE</a>
{{/if}}
<ul id="review_status_dropdown-{{:Id}}" class="dropdown-content">
<li><a class="state-change-button" href="#" data-value="0">NOT COMPLETE</a></li>
<li><a class="state-change-button" href="#" data-value="1">COMPLETE</a></li>
</ul>
</div>
{{/for}}
If you click on a button after running, it opens the drop down just fine. If you select an option that is not set (ie set the COMPLETED button to NOT COMPLETE), it updates the UI, but as the ul is removed from the DOM, clicking on it again does not reveal the drop down. Only the button that is updated is affected.
Can anyone see where I am going wrong?
It looks like Materialize clones the ul and inserts a copy after each activator. Initially there is only one activator (since the other {^{if}}
expression is false so its content is empty) - and the cloned ul is placed next to it inside the {^{if}}
block.
When you click to toggle the status, the contents of that {^{if}}
get removed, and the other {^{if}}
contents get rendered. But the second activator (<a>
tag) has not been 'activated' and so does not have an associated <ul>
.
If you are going to use Materialize alongside JsViews, then you have to deal with Materialize potentially doing DOM manipulation, which can break the JsViews data linking. Having two frameworks which both manipulate the same DOM can lead to collisions or conflicts.
In this case you can resolve the problem by using the visible binding, rather than {^{if}}
:
<div class="dropdown change-state" style="display: block">
<a href="#" data-link="visible{:Status==0}" class="completed-state-box dropdown-button btn btn-flat red" data-constrainwidth="false" data-activates='review_status_dropdown-{{:Id}}'>NOT COMPLETE</a>
<a href="#" data-link="visible{:Status==1}" class="completed-state-box dropdown-button btn btn-flat green" data-constrainwidth="false" data-activates='review_status_dropdown-{{:Id}}'>COMPLETE</a>
<ul id="review_status_dropdown-{{:Id}}" class="dropdown-content">
<li><a class="state-change-button" href="#" data-value="0">NOT COMPLETE</a></li>
<li><a class="state-change-button" href="#" data-value="1">COMPLETE</a></li>
</ul>
</div>
Update:
In response to grayson's Materialize-related issues of 'inline-block' vs 'block', here are a couple of alternatives:
You can data-link directly to the CSS display attribute, and specify the values you want to toggle:
<a data-link="css-display{:Status==1?'inline-block':'none'}" ...>
Or you can create a custom tag that does what you want:
$.views.tags("show", {
render: function(val) {
return val ? "inline-block" : "none"
},
attr: "css-display"
});
and then write:
<a data-link="{show Status==0}" ...>
You can even do:
$.views.tags("show", {
render: function(val, type) {
return val ? type||"inline-block" : "none"
},
attr: "css-display"
});
and write
<a data-link="{show Status==0}" ...>
or
<foo data-link="{show Status==0 'block'}" ...>