KnockoutJS Hide wrapping HTML if condition is met - javascript

I've created a gridview component and if the user provides a title as a parameter, I want it to be wrapped in a bootstrap panel. I don't really want to duplicate my markup so I tried to utilise containerless control syntax to prevent the panel HTML from being added to the DOM.
Here is my attempt:
<!-- ko if: $root.title-->
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title" data-bind="text: $root.title"></h3>
</div>
<div class="panel-body">
<!-- /ko -->
<table class="table table-striped">
<thead>
<tr data-bind="foreach: columns">
<th data-bind="text: headerText"></th>
</tr>
</thead>
<tbody data-bind="foreach: itemsOnCurrentPage">
<tr data-bind="foreach: $parent.columns">
<td data-bind="html: typeof rowText == 'function' ? rowText($parent) : $parent[rowText]"></td>
</tr>
</tbody>
</table>
<nav data-bind="visible: maxPageIndex() > 0">
<ul class="pagination">
<li data-bind="click: previousPage">
<a href="#" aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
<!-- ko foreach: ko.utils.range(0, maxPageIndex) -->
<li data-bind="css: { active: $data == $parent.currentPageIndex }, click: function(){$parent.updateCurrentPageIndex($data)}">
</li>
<!-- /ko -->
<li data-bind="click: nextPage">
<a href="#" aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
</ul>
</nav>
<!-- ko if: $root.title -->
</div>
</div>
<!-- /ko -->
However the result isn't correct. If I invert the condition to ifnot: $parent.title it hides correctly.
Result with if: $parent.title
Any suggestions?

So it seems that knockouts containerless control syntax requires valid HTML syntax to work correctly. So since I didn't close the div tags when I wrapped it, it's throwing a wobbler, understandably.
The solution I've just come up with is using the CSS binding, and removing the panel and panel-default classes if typeof $root.title === 'undefined'
Solution
<div class="panel panel-default" data-bind="css:{'panel panel-default': typeof $root.title !== 'undefined'}">
<div class="panel-heading">
<h3 class="panel-title" data-bind="text: $root.title"></h3>
</div>
<div class="panel-body" data-bind="css:{'panel-body': typeof $root.title !== 'undefined'}">
Not pretty but it's far prettier than duplicating markup.

Related

Ko Bindings after #Html.Partial do not work

I Have the following code cshtml
<div class="container-fluid" id="dvUserData">
<div class="tab-pane fade" id="documentos" role="tabpanel" aria-labelledby="documentos-tab">
#Html.Partial("Documentos")
<div class="col-md-3">
<button type="submit" class="btn btn-primary botao-vert" data-bind="click: editData">SALVAR</button>
</div>
</div>
and the following binding
ko.applyBindings(model, document.getElementById("dvUserData"));
But my data-bind="click: editData" does not work, the click is not performed. But if i put the div class="col-md-3" before the #Html.Partial("Documentos") the bindings work.
I have no idea why, I looked for posts here but could not find anything similar.
Thanks in advance for any help.
---EDITED
The Partial
<div class="row" id="dvDocument">
<form role="form">
<div class="row">
<div class="col-12">
<table class="table tabela-documentos">
<tbody data-bind="foreach: documentsReturn().documentTypes">
<tr class="bg-cinza">
<td>
<div class="aviso-sucesso" data-bind="visible : hasAllDocuments()"></div>
<div class="aviso-critica" data-bind="visible : !hasAllDocuments()"></div> <span data-bind="text: Description"></span>
</td>
<td>
<span class="d-none" data-bind="value: IdTypeDocument"></span>
<a class="btn btn-secondary" data-bind="click: function () { $parent.saveTypeDocument(IdTypeDocument); $('.alerta-form2').hide();}" data-toggle="modal" data-target="#modalDocumento">
ADICIONAR
</a>
</td>
</tr>
<!-- ko foreach: DocumentsArray() -->
<tr>
<td><i class="fa fa-file-text-o" aria-hidden="true"></i> Inclusão: <span data-bind="text: NewDate"></span></td>
<td>
<i class="fa fa-times pointer" data-toggle="modal" data-target="#modal-confirmation" aria-hidden="true" data-bind="visible : !frombase(),
, click: function(){ #*if (confirm('Deseja realmente deletar o documento?')) {*# $parent.savePath(Path); #*}*# }"></i>
</td>
</tr>
<!-- /ko-->
</tbody>
</table>
</div>
</div>
<div class="alert-button">
</div>
</form>
</div>
I forgot to mention that i have another javascript file that bindings the div document with a different model.
Press the F12 and check for any knockout js init (ko.applyBindings) error.
If there is any, fix it and try.
If no error exist, then make sure you have set a value to the model.Name() property by debugging the code.
and check your partial view and see whether you have narrow down the scope/ binding context to another complex property of you model which also has the Name property.
It would be better if you could give more info about partial view binding declaration and the how you are setting / loading the initial values to your model.
After looking for the solution and talk with some friends i found out what was the problem. I was binding the major div, that had the div id "documentos", and another information, and this was the problem.
I remove the binding of dvUserData, and put the binding in all the div that i had, including creating a new div just for the button.
This is how my div "documentos" is now.
<div class="tab-pane fade" id="documentos" role="tabpanel" aria-labelledby="documentos-tab">
#Html.Partial("Documentos")
<div class="alert-button mb-3">
<div id="dvDocumentValidate">
<button type="submit" class="btn btn-primary botao-vert" data-bind="click: editData">SALVAR</button>
</div>
</div>
</div>
And my bindings
ko.applyBindings(model, document.getElementById("dvLoading"));
ko.applyBindings(model, document.getElementById("personaldata"));
ko.applyBindings(model, document.getElementById("address"));
ko.applyBindings(model, document.getElementById("databank"));
ko.applyBindings(model, document.getElementById("dvDocumentValidate"));
ko.applyBindings(model, document.getElementById("corporatedata"));

AngularJS: how to update key of $scope associate array on checkbox select/unselect?

my PHP programming return following JSON via AJAX which is being set in a scrope variable $scope.roleAssigned which then eventually generates an Accordian with a table and checkbox:
$json2 = '{"status":"OK","data":[{"label":"Admin","id":1,"rights":[{"id":"1","label":"create","selected":"1"},{"id":"2","label":"update","selected":"0"},{"id":"3","label":"delete","selected":"0"},{"id":"4","label":"lists","selected":"0"}]},
{"label":"Normal User","id":2,"rights":[{"id":"1","label":"create","selected":"1"},{"id":"2","label":"update","selected":"1"},{"id":"3","label":"delete","selected":"1"},{"id":"4","label":"lists","selected":"0"}]}]}';
Now on button click I want to send back same data back to PHP in JSON. selected field is telling whether someone clicked on checkbox or not. So for instance {"id":"1","label":"create","selected":"1"} could be {"id":"1","label":"create","selected":"0"}
Is there any Angular Way to update key of scope array on checkbox click? I don't want to generate iterating DOM(jQuery way).
HTML
<div class="dynamic" ng-repeat="role in roles | orderBy:sortType:sortReverse | filter:queryRoles ">
<div class="panel panel-default">
<div class="panel-heading">
<h2 class="panel-title text-center">
<a class="accordion-toggle" data-toggle="collapse" data-parent="#accordion" href="#collapse_{{role.id}}">
{{role.label}}
</a>
</h2>
</div>
<div id="collapse_{{role.id}}" class="panel-collapse collapse">
<div class="panel-body text-center">
<table id="roles-table" class="table table-striped" align="center">
<thead>
<tr>
<td>Droit</td>
<td>Action</td>
</tr>
</thead>
<tbody>
<tr ng-repeat="right in role.rights">
<td>{{right.label}}</td>
<td>
<input data-rid="{{role.id}}" type="checkbox" ng-checked="right.selected == 1">
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
Let me know if possible.
You can make use of $parent.$index and $index
<input data-rid="{{role.id}}" type="checkbox" ng-checked="right.selected == 1" ng-click="checkIt($parent.$index, $index, right.selected)">
$scope.checkIt= function(parentIndex, index, selected){
selected = (selected === "1") ? "0" : "1";
$scope.roles[parentIndex].rights[index].selected = selected;
};

accordion toggle collapsing more than targeted divs

In my current piece of code when i try to collapse a letter it collapses the first div of the content when it should collapse all the divs within a letter header - i am not sure how to alter my JavaScript to properly collapse divs within a div.
<div class="accordion-group" data-bind="foreach: Types">
<div class="text_x-large header"> <span data-bind="text:Name()"></span>
</div>
<div class="testing" data-bind="foreach: Users">
<!-- ko if: Letter -->
<div class="text_x-large letterHeader list_accordion_toggle" data-bind="visible: $parent.ShowLetter(), text: Letter"></div>
<!-- /ko -->
<div class="type_list_item smoke_hover" data-bind="template: { name: 'list'}"></div>
</div>
</div>
JavaScript
$(document).on('click', '.list_accordion_toggle', function (event) {
$(this).toggleClass('type_list_item collapse').next().toggle();
});
One approach is to adjust your markup to include a containing DIV element. Like so: jsFiddle
EDIT:
Knockout allows you to set your own markup, so you should be able to modify it like this:
<div class="accordion-group" data-bind="foreach: Types">
<div class="text_x-large header"> <span data-bind="text:Name()"></span>
</div>
<div class="testing" data-bind="foreach: Users">
<!-- ko if: Letter -->
<div class="text_x-large letterHeader list_accordion_toggle" data-bind="visible: $parent.ShowLetter(), text: Letter"></div>
<!-- /ko -->
<div class="wrapper">
<div class="type_list_item smoke_hover" data-bind="template: { name: 'list'}"></div>
</div>
</div>
</div>

knockout weird binding issue in if statement

here's a weird on that I can't seem to find any reference too after trawling the internet for some time.
i'm using knockout to bind a list of images to a view / edit control.
here i have my original attempt
<!-- ko if: Position() == 'gal' -->
<div class="editor-image">
<!-- ko if: $parent.mode() == 'view' -->
<a title="" class="view-image" data-bind="attr: { href : imagePath }">
<!-- /ko -->
<img data-bind="attr: { src : imageThumbnail }" />
<!-- ko if: $parent.mode() == 'view' -->
</a>
<!-- /ko -->
<!-- ko if: $parent.mode() == 'edit' -->
<div>
<a style="display: none;" class="ui-icon ui-icon-zoomin" title="View larger image" href="#">View larger</a>
<a style="display: none;" class="ui-icon ui-icon-zoomout" title="View smaller image" href="#">View Smaller</a>
<a class="ui-icon ui-icon-refresh" title="Delete this image" href="#">Delete image</a>
</div>
<!-- /ko -->
</div>
<!-- /ko -->
the above code adds a tag in if we in view mode then adds controls in if in edit mode and in both situations it will include in img tag. oddly the img src binding doesn't work. but if i do the following it does
<!-- ko if: Position() == 'gal' -->
<div class="editor-image">
<img data-bind="attr: { src : imageThumbnail }" />
<!-- ko if: $parent.mode() == 'view' -->
<a title="" class="view-image" data-bind="attr: { href : imagePath }">
<!-- /ko -->
<!-- ko if: $parent.mode() == 'view' -->
</a>
<!-- /ko -->
<!-- ko if: $parent.mode() == 'edit' -->
<div>
<a style="display: none;" class="ui-icon ui-icon-zoomin" title="View larger image" href="#">View larger</a>
<a style="display: none;" class="ui-icon ui-icon-zoomout" title="View smaller image" href="#">View Smaller</a>
<a class="ui-icon ui-icon-refresh" title="Delete this image" href="#">Delete image</a>
</div>
<!-- /ko -->
</div>
<!-- /ko -->
all i have done here is more the img tag to the top outside the if/endif if/endif and it binds fine. To resolve this i have resorted to
<!-- ko if: Position() == 'gal' -->
<div class="editor-image">
<!-- ko if: $parent.mode() == 'view' -->
<a title="" class="view-image" data-bind="attr: { href : imagePath }">
<img data-bind="attr: { src : imageThumbnail }" />
</a>
<!-- /ko -->
<!-- ko if: $parent.mode() == 'edit' -->
<img data-bind="attr: { src : imageThumbnail }" />
<div>
<a style="display: none;" class="ui-icon ui-icon-zoomin" title="View larger image" href="#">View larger</a>
<a style="display: none;" class="ui-icon ui-icon-zoomout" title="View smaller image" href="#">View Smaller</a>
<a class="ui-icon ui-icon-refresh" title="Delete this image" href="#">Delete image</a>
</div>
<!-- /ko -->
</div>
<!-- /ko -->
which i guess is less code, but its repeated code. and i'm now curious to why my first attempt didn't work.
I would guess knockout traverses the DOM thus the comments need to be in the same level in the DOM tree for knockout to match opening and closing comments.
The following nodes mean that the end comment is in a different level in the DOM to the start comment:
<!-- comment 1 --><!-- end comment 1 -->
Here's the DOM tree for that markup:
|-- A
|---- COMMENT
|-- COMMENT
..and thus knockout cannot find the closing comment tag.
There's nothing wrong with your last code example in my opinion. You can use templates to reduce code duplication though if this is your concern.

<br> become <br> when taken from mongodb meteor

I have a little problem here. my tag became <br> instead when I tried to set the text of label from mongodb.
//my meteor methods
Meteor.methods({
addEventDetails : function(title, details) {
details=details.replace('\n', "<br>");
eventDetails.insert({'title': title, 'details': details});
}
});
//my template function
Template.detailsboard.evt = function(){
return eventDetails.findOne({});
}
//my template
<template name="detailsboard">
<div class="block">
<div class="navbar navbar-inner block-header">
<div class="pull-left"><i class="icon-bookmark"></i><span class="block-header-title">Event Details</span></div>
<div class="pull-right searchbar"></div>
<!-- <div class="pull-right"><span class="badge badge-warning">View More</span></div> -->
</div>
<!-- /block header-->
<!-- block content -->
<div class="block-content-details collapse in">
<ol class="stream-items" id="stream-items-id">
<a class="logo-details" href="/"></a>
<div class="row-fluid" style="padding:10%">
<div class="span12">
<label class="text-inverse details-title">{{evt.title}}</label>
**//problem here**
<p class="text-inverse details-content">this.html({{evt.details}})</p>
</div>
</div>
</ol>
</div>
<!-- /block content -->
</div>
<!-- block header -->
</template>
Use a triple mustache {{{evt.details}}}, because by default Handlebars (or originally Mustache) escapes HTML.

Categories

Resources