I have an image gallery displayed on my page. I need to implement a modal for whenever the user clicks on the images. In the modal I need to show the full size of the selected image. Here is the problem: I have already made the modal work, but when I click on any of the gallery images, the modal shows all of them together in a single modal. I need the modal to only show the one that the user clicked on.
Please note that my webpage is based on AngularJS and PHP. I used ngModal for this modal and that I'm new to using Angular (basically I know nothing, I'm learning), so please be patient with me. Here is my code:
app.js
readApp.controller('newsGallery', function($scope) {
$scope.myData = {
modalShown: false,
}
$scope.logClose = function() {
console.log('close!');
};
$scope.toggleModal = function() {
$scope.myData.modalShown = !$scope.myData.modalShown;
};
});
HTML
<div ng-controller='newsGallery'>
<modal-dialog show='myData.modalShown' width='75%' height='80%' on-close='logClose()'>
<div ng-repeat = "i in idsBlobs" >
<img src="php/visualizar_archivo.php?id={{i.id}}">
</div>
</modal-dialog>
<div class="row" style="display:flex; flex-wrap: wrap;">
<div class = "col-md-4" ng-repeat = "i in idsBlobs" >
<div class="news-image" align="center">
<img src="php/visualizar_archivo.php?id={{i.id}}" class = "img-responsive img-rounded" ng-click='toggleModal();'>
</div>
</div>
</div>
</div>
One way to have the image that the user clicked on shown in the modal is to introduce a scope variable e.g. $scope.selectedImage. Next, in the function toggleModal(), accept an argument for the image and set that scope variable to that argument.
$scope.toggleModal = function(image) {
$scope.myData.modalShown = !$scope.myData.modalShown;
$scope.selectedImage = image;
};
Next update the call to that function in the ng-click handler:
<img src="php/visualizar_archivo.php?id={{i.id}}" ng-click='toggleModal(i);' class = "img-responsive img-rounded">
Then in the modal markup, show that selected image.
<modal-dialog show='myData.modalShown' width='75%' height='80%' on-close='logClose()'>
<img src="php/visualizar_archivo.php?id={{selectedImage.id}}">
</modal-dialog>
That way the modal will only show the image that the user clicked on, instead of all images in the list.
See a demonstration of this below.
readApp = angular.module('readApp', ["ngModal"]);
readApp.controller('newsGallery', function($scope) {
$scope.idsBlobs = [{
"id": 'MA',
"src": "http://www.animatedimages.org/data/media/96/animated-lighthouse-image-0032.gif"
},
{
"id": "MU",
"src": "http://icons.iconarchive.com/icons/aha-soft/large-home/128/Museum-icon.png"
}
];
$scope.myData = {
modalShown: false
}
$scope.logClose = function() {
console.log('close!');
};
$scope.toggleModal = function(image) {
$scope.myData.modalShown = !$scope.myData.modalShown;
$scope.selectedImage = image;
};
});
.img-thumb {
height: 48px;
width: 48px;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="//elliott.andrewz.org/cdn/ng-modal.min.js"></script>
<link href="//elliott.andrewz.org/cdn/ng-modal.css" rel="stylesheet" />
<div ng-app="readApp" ng-controller="newsGallery">
<modal-dialog show="myData.modalShown" width="75%" height="80%" on-close="logClose()">
<img src="{{ selectedImage.src }}" />
</modal-dialog>
<div class="row" style="display:flex; flex-wrap: wrap;">
<div class="col-md-4" ng-repeat="i in idsBlobs">
<div class="news-image" align="center">
<img src="{{ i.src }}" class="img-responsive img-rounded img-thumb" ng-click="toggleModal(i);" />
</div>
</div>
</div>
</div>
Related
I want to show a spinner modal in a vue 3 app when the page preloader gets all the data from the server and when it finishes I want to hide the spinner.
My preloader:
async preload(to) {
Spinner.showSpinner();
let data = await Manager.getFilteredList({
filter: [],
order: {},
pageinfo: { pagenum: 1, pagelength: globalVars.EVENT_PER_PAGE },
}).finally(() => Spinner.hideSpinner());
to.params.list= data.list;
to.params.totalCount = data.totalcount;
return to;
}
Spinner.js
import { Modal as BSModal } from 'bootstrap';
class Spinner {
showSpinner() {
let modal = BSModal.getInstance(document.getElementById('mdlSpinner'));
if (!modal) {
modal = new BSModal(document.getElementById('mdlSpinner'));
}
modal.show();
}
hideSpinner() {
const modal = BSModal.getInstance(document.getElementById('mdlSpinner'));
modal.hide();
}
}
export default new Spinner();
App.vue
<template>
<div class="home">
<div class="modal" tabindex="-1" id="mdlSpinner">
<div
class="spinner-border text-info"
role="status"
style="width: 3rem; height: 3rem"
>
<span class="visually-hidden">Loading...</span>
</div>
</div>
<div id="login-teleport" />
<Navbar />
<div class="cam-container">
<div class="sidebar">
<Sidemenu />
</div>
<div class="content">
<router-view />
<div id="popup-teleport" />
</div>
</div>
</div>
</template>
This way on the page I get an error:
TypeError: Illegal invocation
at Object.findOne (bootstrap.esm.js:1017)
at Modal._showElement (bootstrap.esm.js:2928)
at eval (bootstrap.esm.js:2851)
at execute (bootstrap.esm.js:275)
at eval (bootstrap.esm.js:2557)
at execute (bootstrap.esm.js:275)
at executeAfterTransition (bootstrap.esm.js:281)
at Backdrop._emulateAnimation (bootstrap.esm.js:2627)
at Backdrop.show (bootstrap.esm.js:2556)
at Modal._showBackdrop (bootstrap.esm.js:3032)
And also I guess if it would even work my spinner would still show because it is permanently coded into App.vue (?).
What would be the ideal implementation of a Spinner which I could use in preloaders and also in vue templates - for example when I click on a button to fetch data.
Thank you for your answers in advance!
I have ng-repeat and ng-class for each element. I use it for selecting elements (add border for selected).
<div class="main-block_channel_create">
<section class="parent_messageList cancelDelete">
<div id="section_animate" class="messagesList_block cancelDelete" infinite-scroll='messagelist.nextSet()' infinite-scroll-listen-for-event='anEvent' infinite-scroll-distance='100'>
<!-- User Images List -->
<a href="" class="messageBl message_preview animated" ng-repeat='preview in messagelist.previewList'>
<div class="image_container_preview" ng-class="{community_select: item.isSelected}" ng-click='communitySelect(preview)' attr="{{preview.preview_id}}">
<img class="deleteMessageBtn" src="images/close_icon.svg" ng-click="deleteMessage(preview.message_id)">
<div class="spinner_container">
<!--<img src="/images/logo-80.svg" class='spinner_img'>-->
<img class="spinner_logo_vertical" src="images/logo_vertical-part.svg" alt="">
<img class="spinner_logo_left" src="images/logo_left-part.svg" alt="">
<img class="spinner_logo_right" src="images/logo_right-part.svg" alt="">
</div>
<img class="message_preview-image" src="{{preview.image}}" alt="">
<!-- Tсли нет изображения — показываем текст -->
<div class="message_preview-text MediumNormalJunior" ng-if="!preview.image">
<div class="message_preview-text-inner" ng-if='preview.name'>
{{preview.name}}
</div>
<!-- если нету и текста показываем empty-->
</div>
<div class="empty_message" ng-if='!preview.text && !preview.image'>
<!--<h4>Empty</h4> -->
</div>
</div>
<div class="stats" ng-show='preview.total_score > 0'>
<p>{{preview.total_score}}</p>
</div>
</a>
<footer class="listFooter">
</footer>
</div>
</section>
sass
.community_select
border: 3px solid white
directive
(function(){
'use strict';
angular
.module('buzz')
.directive('channelcreate', channelcreate);
function channelcreate($rootScope, $location, $timeout, $cookies, $window, communityApiService, getCommunities){
return{
restrict: "E",
replace: true,
scope: true,
templateUrl: '/templates/channelCreate/channelCreate.html',
link: function($scope){
// $rootScope.showChannelCreate = null;
// Select communities for create new channel
$scope.communityList = [];
$scope.communitySelect = function(communityId){
$scope.selected = false;
if ($scope.communityList.indexOf(communityId) == -1){
$scope.communityList.push(communityId);
} else {
$scope.communityList.pop(communityId)
}
console.log($scope.communityList);
};
// all messages preview are loaded from messagesLoadFactory
$scope.messagelist = new getCommunities();
}
};
};
})();
I can identify each div by id when it is clicked. How can I change only one element, but not all of them?
Following your login... an option can be to check if the element's id is in the list of selected elements with communityList.indexOf(preview.id) != -1 so your ng-class would look like:
ng-class="{community_select: communityList.indexOf(preview.id) != -1}"
Edit
Also, when deleting an id from the $scope.communityList make sure you first find its index and then remove it with splice.
Now the portion for removing/adding the id would look like this:
// ... content omitted
$scope.communitySelect = function(communityId) {
$scope.selected = false;
var index = $scope.communityList.indexOf(communityId);
if (index == -1) {
$scope.communityList.push(communityId);
} else {
$scope.communityList.splice(index, 1)
// ^^^ ^
// remove starting_here one_element
}
console.log($scope.communityList);
};
// ... content omitted
I guess you want to keep selection on screen to highlighted, for the same I don't think so you need to maintain extra collection list. Rather you can add flag isSelected on that record and toggle it based on user click.
HTML
I have and ng-repeat and ng-class for each element. I use it for selecting elements (add border for selected).
<a href="" class="messageBl message_preview animated"
ng-repeat='preview in messagelist.previewList'>
<div class="image_container_preview"
ng-class="{community_select: item.isSelected}"
ng-click='communitySelect(preview)'
attr="{{preview.preview_id}}">
</a>
Code
$scope.communitySelect = function(communityId) {
item.isSelected = !item.isSelected;
};
And whenever you need list of selected previews, you can easily loop over the previews collection and grab those ones who have isSelected flag ticked.
var selected = $scope.previews.map(i => i.isSelected);
I have an ng-repeat which repeats a bunch of products in a dropdown.
On hover over these guys, I want to bounce the image that I'm hovering into a container outside of the dropdown.
MARKUP:
<div class="quick-view-filters-container" ng-controller="setZoomDrop">
<!-- zoomed image container from ng-repeat below -->
<div class="product--shade__image-zoom--container">
<img class="ng-hide" ng-show="zoom=1" image="option.product" step="1" always="1" />
</div>
<!-- dropdown -->
<div class="product-select-shades-container" ng-repeat="attribute in attributes | onlyAttrsWithManyOptions | orderBy:$parent.$parent.$parent.configurableOrder">
<h4>Products:</h4>
<div class="product--options_list">
<div ng-repeat="option in attribute.options" class="product--option_item" ng-if="option.product">
<span class="product--shade__image ">
<!-- image -->
<img class="{{:: attribute.code == 'lamp_colour_config' ? 'zoomed' : ''}}" image="option.product" step="1" ng-mouseenter="setZoom(1)" ng-mouseleave="setZoom(0)" always="1" />
</span>
</div>
</div>
</div>
</div>
Controller:
window.app.controller('setZoomDrop', ['$scope', function($scope) {
var zoom = null;
$scope.setZoom = function(number) {
$scope.zoom = number;
};
}]);
Workings:
You can see in the markup that I am setting a variable to 1 or 0 dependable on whether a product in the dropdown is hovered over.
This I have tried to use in the zoomed image container.
option.product in the zoomed image container will not work as I don't know how to pull out the active image from the ng-repeat.
in your setZoom function, instead of passing a number pass the option object if mouseenter or null if mouseleave:
$scope.curOpt = null;
$scope.setZoom = function(option) {
$scope.curOpt = option;
}
Then in your html:
<img ng-show="curOpt" image="curOpt.product" step="1" always="1" />
Simply, I have an array of objects, [images], that I display perfectly fine on Ng-repeat. When I click one div, I want to see the 'id' property of the particular image that was clicked on in the template.
Function in controller:
angular.forEach(value, function (value, key) {
$scope.images.push({
id: i,
src: ("data:image/jpeg;base64," + value)
});
i = i + 1;
}
$scope.seeOne = function () {
console.log(image.id);
}
My template:
<div id="galscrolldiv" ng-repeat="image in images">
<div ng-click="seeOne()">
<img ng-src="{{images[$index].src}}" width="100%" />
</div>
</div>
Send the image as parameter from the view/template. Then you can access the image properties of the clicked image.
You've missed the ) of forEach.
Controller:
angular.forEach(value, function (value, key) {
$scope.images.push({
id: i,
src: ("data:image/jpeg;base64," + value)
});
i = i + 1;
});
// Get the parameter i.e. image that is clicked
$scope.seeOne = function (image) {
console.log(image.id);
};
View/Template:
<div id="galscrolldiv" ng-repeat="image in images">
<div ng-click="seeOne(image)">
<!-- ^^^^^-->
<img ng-src="{{images[$index].src}}" width="100%" />
</div>
</div>
You can also send the $index property from template
<div id="galscrolldiv" ng-repeat="image in images">
<div ng-click="seeOne($index)">
<!-- ^^^^^-->
<img ng-src="{{images[$index].src}}" width="100%" />
</div>
</div>
Then inside the controller
$scope.seeOne = function (index) {
console.log($scope.images[index].id);
};
But this may cause issue while using filter.
So in case of using filter avoid $index.
So the situation is as follows:
I have an input bar where a user can search up a business name or add a person's name (and button to select either choice). Upon hitting enter I want to append a unique instance of a template (with the information entered by the user added). I have 2 templates I've created depending of if the user is searching for a business or a person.
One approach I've thought about is creating an object with the data and adding it with ng-repeat, however I can't seem to get the data loaded, and even then don't know how I can store reference to a template in my collection.
The other idea I've come across is adding a custom directive. But even then I've yet to see an example where someone keeps appending a new instance of a template with different data.
Here is the code so far:
payments.js:
angular.module('payment-App.payments',['ngAutocomplete'])
.controller('paymentController', function($scope, $templateRequest/*, $cookieStore*/) {
$scope.businessID;
$scope.address;
$scope.isBusiness = false;
$scope.payees = [];
$scope.newPayee = function () {
this.businessID = $scope.businessID;
this.address = $scope.address;
}
$scope.submit = function () {
var text = document.getElementById("businessID").value.split(",");
$scope.businessID = text[0];
$scope.address = text.slice(1).join("");
$scope.newPayee();
}
$scope.addPayee = function () {
$scope.submit();
$scope.payees.push(new $scope.newPayee());
console.log($scope.payees);
}
$scope.selectBusiness = function () {
//turns on autocomplete;
$scope.isBusiness = true;
}
$scope.selectPerson = function () {
//turns off autocomplete
$scope.isBusiness = false;
}
$scope.fillAddress = function () {
// body...
}
})
.directive("topbar", function(){
return {
restrict: "A",
templateUrl: 'templates/businessTemplate.html',
replace: true,
transclude: false,
scope: {
businessID: '=topbar'
}
}
})
payments.html
<h1>Payments</h1>
<section ng-controller="paymentController">
<div>
<div class="ui center aligned grid">
<div class="ui buttons">
<button class="ui button" ng-click="selectBusiness()">Business</button>
<button class="ui button arrow" ng-click="selectPerson()">Person</button>
</div>
<div class="ui input" ng-keypress="submit()">
<input id="businessID" type="text" ng-autocomplete ng-model="autocomplete">
</div>
<div class="submit">
<button class="ui button" id="submit" ng-click="addPayee()">
<i class="arrow right icon"></i>
</button>
</div>
</div>
<div class="search"></div>
<div class="payments" ng-controller="paymentController">
<li ng-repeat="newPayee in payees">{{payees}}</li>
</div>
<!-- <topbar></topbar> -->
</div>
(example template)
businessTemplate.html:
<div class="Business">
<div class="BusinessName" id="name">{{businessID}}</div>
<div class="Address" id="address">{{address}}</div>
<button class="ui icon button" id="hoverbox">
<i class="dollar icon"></i>
</button>
</div>
I ended up using a workaround with ng-repeat here. Still curious about the original question though.