KnockoutJS visible binding not working in IE7 - internet-explorer-7

I have a viewmodel with the following computed observable:
var showBackNavArrow = ko.computed(function () {
if (questionIdx() === 0) {
return false;
}
return true;
}, this);
// Elsehwere in the file....
var questionIdx = ko.observable(0);
and the following in my HTML:
<td>
<button title="Click to view previous question" id="previous" class="previous" data-bind="visible: showBackNavArrow">Previous</button>
</td>
In IE7, when viewing my web page, the above button is never shown even though the computed observable returns true.
In IE8+, Chrome and FireFox the button is displayed.
Why does the "visible" binding not work in IE7 here?
EDIT:
I'm using KnockoutJS version 2.1.0.

Please, try to wrap visible with single quotes in ViewModel:
<td>
<button ... data-bind="'visible': showBackNavArrow">Previous</button>
</td>

Related

Modal is closed when cursor is released outside the modal after Chrome update (angularjs and bootstrap-ui)

Sometimes when I want to quickly select the entire text of an input (within a modal), I begin selecting from the end of the text and move the mouse to the left until the entire text is selected and then I release.
Sometimes this release will occur outside the modal because the mouse movement is fast.
Picture describing the movement:
The problem is that the modal is closed when I release outside.
Question: how can I prevent the modal from closing when releasing outside?
I'm okay with the modal being closed with a click outside. But not okay with the release event.
I'm using:
angularjs 1.5.8
angular-bootstrap 2.5.0 (aka bootstrap-ui)
bootstrap 3.3.7 (only css!!! not js, because js is provided by the above)
Update:
I've created a plunkr and a GIF:
https://plnkr.co/edit/mxDLAdnrQ4p0KKyw?p=info
<div class="modal-body">
<div class="form-group">
<label for="">Foo</label>
<input type="text" class="form-control input-sm" ng-model="foo">
<p>Do this: select the text from right to left and release the mouse outside the modal.</p>
</div>
</div>
GIF:
Update 2
I have new information! This started happening after the last Goole Chrome update! I tried with another computer that had the previous version of Chrome and the modal doesn't close.
//prevent modal close when click starts in modal and ends on backdrop
$(document).on('mousedown', '.modal', function(e){
window.clickStartedInModal = $(e.target).is('.modal-dialog *');
});
$(document).on('mouseup', '.modal', function(e){
if(!$(e.target).is('.modal-dialog *') && window.clickStartedInModal) {
window.preventModalClose = true;
}
});
$("#modal").on("hide.bs.modal", function (e) {
if(window.preventModalClose){
window.preventModalClose = false;
return false;
}
});
The original repository has been archived and no contributions are accepted.
I forked a version and added my fixes for those who are interested:
https://github.com/peteriman/bootstrap
The comparison below:
https://github.com/angular-ui/bootstrap/compare/master...peteriman:modal-patch
= // moved from template to fix issue #2280
- element.on('click', scope.close);
+ var ignoreClick = false;
+ element.on('mousedown', function(evt1) {
+ element.one('mouseup', function(evt2) {
+ if (evt1.target !== evt2.target)
+ ignoreClick = true;
+ });
+ });
+ element.on('click', function(){
+ if (ignoreClick) ignoreClick = false;
+ else scope.close.apply(this, arguments);
+ });
As mousedown and mouseup events trigger before click event, the code checks if mousedown and mouseup are on the same element. If on different elements, it sets ignoreClick=true for the click event to not trigger.
Maintains backward compatibility for click event for existing codes that calls element.click() programmatically.
Original problem:
https://plnkr.co/edit/mxDLAdnrQ4p0KKyw?p=info&preview
Solution by me: (plkr, modal.js, line 103-114)
https://plnkr.co/edit/V42G9NcTUnH9n9M4?p=info&preview
I updated only the code referring to "Modal.js" in bootstrap.js and bootstrap.min.js
Corrected version:
* Bootstrap: modal.js v3.4.1
* https://getbootstrap.com/docs/3.4/javascript/#modals
bootstrap.js print
Yes, this started happening again after the last Goole Chrome update Version 74.0.3729.169, is this a bug with Chrome we can't fix and that we'll just have to wait for a Chrome update for it to be resolved?
or a bootstrap maintainer will update the code for fixing this?
Issue url: https://github.com/twbs/bootstrap/issues/28844
This problem is not recent is already mentioned on github
https://github.com/angular-ui/bootstrap/issues/5810
the following solution works very well with small improvements if necessary.
$rootScope.$watch(() => document.querySelectorAll('.modal').length, val => {
//everytime the number of modals changes
for (let modal of document.querySelectorAll('.modal')) {
if ($uibModalStack.getTop().value.backdrop !== 'static') { // Testing if the
modal is supposed to be static before attaching the event
modal.addEventListener('mousedown', e => {
if (e.which === 1) {
$uibModalStack.getTop().key.dismiss()
}
})
modal.querySelector('.modal-content').addEventListener('mousedown', e => {
e.stopPropagation()
})
}
}
if (val > 0) {
$uibModalStack.getTop().value.backdrop = 'static'
}
})
Another solution on the same principle that keeps the draggrable footer and header of the modal
$rootScope.$watch(function () {
return $document.find('.modal').length;
}, function (val) {
if(openedWindows.top() ) {
var modal = $document.find('.modal');
angular.forEach(modal, function(value) {
if ($modalStack.getTop().value.backdrop !== 'static') {
value.addEventListener('mousedown', function (e) {
if (value === e.target && e.which === 1 && openedWindows.top()) {
$modalStack.getTop().key.dismiss();
}
});
}
});
if (val>0) {
$modalStack.getTop().value.backdrop = 'static';
}
}
});
I'm using Bootstrap v3.0.0 and ran into the same problem. In the end, I had to change a click event to a mousedown event.
In my bootstrap.js file, under the modal.js section, I changed this.$element.on('click.dismiss.modal', $.proxy(function (e) to this.$element.on('mousedown.dismiss.modal', $.proxy(function (e). and everything appears to be working. You may also have to change this in the bootstrap.min.js file.
Note, this will immediately close the modal on mouse down of backdrop so if for some reason you want a user to be able to click down on the backdrop, then drag the mouse and release on the modal, this will not work.
Have you tried using backdrop: 'static'. I think that should do the trick. It is present in the documentation here
Add css padding around modal window and resize it larger. Click outside still works but releasing mouse while dragging over the edge won't close it.
I had a similar situation with range slider. leaving click during slide outside the modal closes it. so I removed data-toggle="modal" and data-target="#mymodal" and added a click event with extra parameters
jQuery('button#modal_toggler').click(function(){
jQuery('#myModal').modal({
backdrop: 'static',
keyboard: false
})
})
backdrop to disable modal close on clicking outside
keyboard this is for my scenario, to disable keyboard entry for closing modal
I have figured out different way to solve the problem, idk if it will cause a problem later but anyway it works, so basically, I put modal-dialog to another <div> object (I call it modal-helper) and then put it to modal. The modal-helper element width and height are inherited (100%) as default but there is small space on top so you can use some margin and padding to close it.
<div class="modal fade" id="login-modal" tabindex="-1" aria-labelledby="loginModalLabel" style="display: none;" aria-hidden="true">
<div id="modal-helper" style="pointer-events: auto;">
<div class="modal-dialog">
...
</div>
</div>
</div>
Then I have used some JS to hide modal when modal-helper (as backdrop) is clicked (by the 'clicked' I mean when pointerup event triggered after pointerdown event on modal-helper).
The code below sets the value of isPointerDownToModalHelper true when pointerdown event triggered on modal-helper, then when the pointerup event triggered on any object it hides the modal and sets the value of isPointerDownToModalHelper back to false:
var login_modal_helper = document.getElementById('modal-helper')
var isPointerDownToModalHelper = false;
addEventListener('pointerdown', (event) => {
var objectID = event['path']['0']['id'];
if (objectID === login_modal_helper.id) { // if pointer was over modal-helper
isPointerDownToModalHelper = true;
}
});
addEventListener('pointerup', (event) => {
if (isPointerDownToModalHelper === true) {
isPointerDownToModalHelper = false;
$('#login-modal').modal('hide'); // hide the modal
}
});
It seems to work fine for now, I hope it can help someone :).

Creating a "Like" button with Angular.js

Im trying to create a like button with Angular.js.
(It is just a heart icon. default color is white = NOT liked. It is red when liked. Like/unlike is toggled by a click)
I get some data from my web service that has also an array of some ID's. These ID's are the ones that clicked the like button before.
Then i populate the DOM with the ng-repeat directive according to the data retrieved from the web service.
I attach the button a ng-class that sets the proper class and a ng-click directive that is supposed to somehow change the class too.
* I cant connect between the ng-class and the ng-click result.
some code:
<div ng-repeat="photo in photos track by photo._id">
<button ng-class="{carouselFooterButtonLikeActive : initLike(photo)}" ng-click="like(photo, this)">
<i class="icon ion-heart"></i>
</button>
</div>
Controller:
// Handle like button click
$scope.like = function(photo, photoScope){
HOW CAN I AFFECT THE NG-CLASS FROM HERE?
}
$scope.initLike = function(photo){
if(photo.likes.indexOf($localstorage.getObject('userInfo').id) > -1) {
$scope.liked = true;
return true;
}
$scope.liked = false;
return false;
}
Edit: added a possible data retrieved from the web service
{
photos: [
{
src: "src1.jpg",
likes:[111,222,333]
},
{
src: "src2.jpg",
likes:[]
}
]
}
You can use as a flag some additional property that will be initially undefined on each photo element - say photo.liked. When user clicks it, $scope.like function sets this property to true. Then ng-class evaluates photo.liked to true and adds carouselFooterButtonLikeActive class to button element.
The code is as follows:
In the template:
<button ng-class="{'carouselFooterButtonLikeActive' : photo.liked}" ng-click="like(photo, this)">
In the controller:
$scope.like = function(photo, photoScope){
photo.liked = true;
}
UPD
Say you have photos array:
[
{'src':'bla-bla.jpg', liked: true, id: 8347},
{'src':'foo-bar.jpg', id: 45},
{'src':'baz-baz.jpg', id: 47}
]
then only the first one will be shown with button.carouselFooterButtonLikeActive class, thanks to ng-class evaluation expression.
UPD2
If photo.likes is an array, you can use:
//template
ng-class="{'carouselFooterButtonLikeActive' : (photo.likes && photo.likes.length >0)}"
//controller
$scope.like = function(photo, photoScope){
photo.likes.push(someUserID);
}

add conditionally an Angular Bootstrap popover inside an ng-repeat

I'm using AngularJS with Angular UI Bootstrap.
In my template i need to show a table, i create it with an ng-repeat, I need to add a popover on click only for certain cells.
I made something like this example:
popover example inside ng-repeat in plunker
How is the better way to have the popover conditionally only in certain cells?
Check the working demo: Plunker. Only the cell with value > 5.0 will show popover (in green background color).
Define a function on the $scope:
$scope.filterCells = function (v) {
return v > 5.0 ? 'mouseenter' : 'none';
};
And the td HTML:
<td data-ng-repeat="v in getRowData(row)" class="zscore"
ng-class="{'show-popup': filterCells(v)}" popover="{{zscores[row][$index]}}"
popover-trigger="{{ filterCells(v) }}"
popover-append-to-body="true" popover-title="zScore">
{{ v | number:1 }}
</td>
Angular 8.2.0 + ng-bootstrap and the Ngb Popover directive
I came across this question while trying to fix my issue, so I'm including my solution here.
I had an issue using the triggers property to conditionally show/hide popovers. It turns out that the triggers value is consumed by the popover in ngOnInit, so it does not show/hide the popover after the component is already initialized.
I found that ngbPopover has a property called disablePopover that accomplishes what I need instead of using triggers.
https://ng-bootstrap.github.io/#/components/popover/api
Before
HTML
<div
ngbPopover="Hello, World!"
[triggers]="triggers">
</div>
TypeScript
private readonly TRIGGERS_ENABLED = 'mouseenter:mouseleave';
private readonly TRIGGERS_DISABLED = 'none';
public triggers = TRIGGERS_DISABLED;
someEvent() {
if (someConditional) {
this.triggers = TRIGGERS_DISABLED;
} else {
this.triggers = TRIGGERS_ENABLED;
}
}
After
HTML
<div
ngbPopover="Hello, World!"
triggers="mouseenter:mouseleave"
[disablePopover]="disablePopover">
</div>
TypeScript
public disablePopover = true;
someEvent() {
if (someConditional) {
this.disablePopover = false;
} else {
this.disablePopover = true;
}
}

The array model is not binding correctly to the view

I'm having problem to make my angularjs app model bind correctly to the view.
I have this methods in the controller to enable/disable the editing of the informations of a single marketplace:
$scope.shopEditing = function (marketplaceId) {
if ( ! $scope.settings) {
return false;
}
return $scope.shopEditingRegistry[marketplaceId];
};
$scope.toggleShopEditing = function (marketplaceId) {
if ( ! $scope.settings) {
return;
}
$scope.shopEditingRegistry[marketplaceId] = ! $scope.shopEditingRegistry[marketplaceId];
};
Then there's the piece of view that should be hidden when the editing for the specific marketplace is enabled:
<div ng-repeat="shopInformations in settings.shops.list">
<div class="line" ng-hide="{{shopInformations.isShopConnected || shopEditing(shopInformations.marketplaceId)}}">
{{marketplaceName(shopInformations.marketplaceId)}} shop not connected
<a class="button buttonGrayThin" ng-click="toggleShopEditing(shopInformations.marketplaceId)">Add</a>
</div>
</div>
The problem is that when I click on the button that is connected to toggleShopEditing(), the model is updated successfully, the shopEditing() is called correctly, but the view is not refreshed correctly.
The 'div class="line" ' is not being hidden.
I'm getting crazy. Any idea why this is happening?
Thanks a lot!
As suggested by #charlietfl it was enough to remove the {{ }} within ng-hide.

How to expand/collapse all rows in Angular

I have successfully created a function to toggle the individual rows of my ng-table to open and close using:
TestCase.prototype.toggle = function() {
this.showMe = !this.showMe;
}
and
<tr ng-repeat="row in $data">
<td align="left">
<p ng-click="row.toggle();">{{row.description}}</p>
<div ng-show="row.showMe">
See the plunkr for more code, note the expand/collapse buttons are in the "menu".
However, I can't figure out a way to now toggle ALL of the rows on and off. I want to be able to somehow run a for loop over the rows and then call toggle if needed, however my attempts at doing so have failed. See them below:
TestCase.prototype.expandAllAttemptOne = function() {
for (var row in this) {
if (!row.showMe)
row.showMe = !row.showMe;
}
}
function expandAllAttemptOneTwo(data) {
for (var i in data) {
if (!data[i].showMe)
data[i].showMe = !data[i].showMe;
}
}
Any ideas on how to properly toggle all rows on/off?
Using the ng-show directive in combination with the ng-click and ng-init directives, we can do something like this:
<div ng-controller="TableController">
<button ng-click="setVisible(true)">Show All</button>
<button ng-click="setVisible(false)">Hide All</button>
<ul>
<li ng-repeat="person in persons"
ng-click="person.visible = !person.visible"
ng-show="person.visible">
{{person.name}}
</li>
</ul>
</div>
Our controller might then look like this:
myApp.controller('TableController', function ($scope) {
$scope.persons = [
{ name: "John", visible : true},
{ name: "Jill", visible : true},
{ name: "Sue", visible : true},
{ name: "Jackson", visible : true}
];
$scope.setVisible = function (visible) {
angular.forEach($scope.persons, function (person) {
person.visible = visible;
});
}
});
We are doing a couple things here. First, our controller contains an array of person objects. Each one of these objects has a property named visible. We'll use this to toggle items on and off. Second, we define a function in our controller named setVisible. This takes a boolean value as an argument, and will iterate over the entire persons array and set each person object's visible property to that value.
Now, in our html, we are using three angular directives; ng-click, ng-repeat, and ng-show. It seems like you already kinda know how these work, so I'll just explain what I'm doing with them instead. In our html we use ng-click to set up our click event handler for our "Show All" and "Hide All" buttons. Clicking either of these will cause setVisible to be called with a value of either true or false. This will take care of toggling all of our list items either all on, or all off.
Next, in our ng-repeat directive, we provide an expression for angular to evaluate when a list item is clicked. In this case, we tell angular to toggle person.visible to the opposite value that it is currently. This effectively will hide a list item. And finally, we have our ng-show directive, which is simply used in conjunction with our visible property to determine whether or not to render a particular list item.
Here is a plnkr with a working example: http://plnkr.co/edit/MlxyvfDo0jZVTkK0gman?p=preview
This code is a general example of something you might do, you should be able to expand upon it to fit your particular need. Hope this help!

Resources