How to use useRef to toggle between images - reactjs

Given an image gallery, where the user can click an image and the selected image will be shown in the gallery below it, how can I use useRef to replace the image in the gallery div with the selected image on click? The idea is that the images will be populating the gallery at the top from an array, so each image will presumably have the useRef applied to it?
HTML
<div class="wrapper">
<div class="row">
<div class="column">
<img src="imageurl" alt="water" style="width:100%" onclick="myFunction(this);">
</div>
<div class="column">
<img src="imageurl2" alt="tree" style="width:100%" onclick="myFunction(this);">
</div>
<div class="column">
<img src="imageurl3" alt="snow" style="width:100%" onClick="myFunction(this);">
</div>
<div class="column">
<img src="imageurl4" alt="mountain" style="width:100%" onclick="myFunction(this);">
</div>
<div class="column">
<img src="imageurl5" alt="tree2" style="width:100%" onclick="myFunction(this);">
</div>
</div>
<div class="container">
<span onclick="this.parentElement.style.display='none'" class="closebtn">×</span>
<img id="expandedImg" style="width:100%">
<div id="imgtext"></div>
</div>
</div>
Function to toggle large image visibility
const myFunction = imgs => {
const imageRef = useRef();
var expandImg = document.getElementById("expandedImg");
var imgText = document.getElementById("imgtext");
expandImg.src = imgs.src;
imgText.innerHTML = imgs.alt;
expandImg.parentElement.style.display = "block";
}
The current state can be seen here: jsFiddle

As the React Docs state: Don’t Overuse Refs
Your code snippet is not what refs should be used for. In React, you should make things interactive via state changes, not by fiddling with the DOM, React is designed to do that for you.
However from that jsfiddle, it looks like you aren't even using React? I am unsure why you are using useRef at all?
UPDATE: Code fix
By using function to define your function (instead of an arrow function), it gets hoisted so your HTML elements can pick it up, and the getElementById method gets you the element reference you need.
Just replace all your js with:
var expandImg = document.getElementById("expandedImg");
var imgText = document.getElementById("imgtext");
function myFunction(imgs) {
expandImg.src = imgs.src;
imgText.innerHTML = imgs.alt;
expandImg.parentElement.style.display = "block";
}
...and your jsfiddle works.

Related

AngularJS- How to handle each button created by ng-repeat

I am new to AngularJS.
I have created <li> to which I used ng-repeat.
<li> contains images and buttons like like, comment and share which is inside <li> and created by ng-repeat.
I have made function which will replace empty like button to filled like button (By changing background image of button).
But problem is this trigger applies to only first like button and other buttons does not change.
How can I fix this?
Code:
HTML:
<html>
<body>
<ul>
<li ng-repeat="media in images"><div class="imgsub">
<label class="usrlabel">Username</label>
<div class="imagedb">
<input type="hidden" value="{{media.id}}">
<img ng-src="{{ media.imgurl }}" alt="Your photos"/>
</div>
<!-- <br><hr width="50%"> -->
<div class="desc">
<p>{{media.alt}}</p>
<input type="button" class="likebutton" id="likeb" ng-click="like(media.id)" ng-dblclick="dislike(media .id)"/>
<input type="button" class="commentbutton"/>
<input type="button" class="sharebutton"/>
</div>
</div> <br>
</li><br><br><br>
</ul>
</body>
</html>
JS:
$scope.like = function(imgid)
{
document.
getElementById("likeb").
style.backgroundImage = "url(src/assets/like-filled.png)";
alert(imgid);
}
$scope.dislike = function(imgid)
{
document.
getElementById("likeb").
style.backgroundImage = "url(src/assets/like-empty.png)";
}
Thanks for help & suggestions :)
The id for each button should be unique but in your case, it's the same for all buttons ('likeb').
You can set the value of the attribute 'id' for each button dynamically by using '$index' and passing '$index' to the functions as follows:
<input type="button" class="likebutton" id="{{$index}}" ng-click="like($index)" ng-dblclick="dislike($index)"/>
Then in your controller, you can use the functions with the passed value.
For example,
$scope.like = function(index)
{
document.
getElementById(index).
style.backgroundImage = "url(src/assets/like-filled.png)";
}
Another good alternative in your case would be to use the directive ngClass.
use 2 css class for styling liked and disliked state, and then put the class conditionally with ng-class instead of DOM handling. and if you really want to perform a DOM operation (I will not recommend) then you can pass $event and style $event.currentTarget in order to perform some operation on that DOM object.

toggle extra detail for a record inside an ngRepeat

I'm working on a project where the client has supplied a pile of html where I need to plugin the data from our database and have hit a problem that I'm finding difficult to solve....
So first problem is with routing
<div ng-repeat="class in vm.classes">
<div class="class-overview">
<a href="#">
<span class="class-title">{{class.description}}</span>
... more stuff here
</a>
</div>
<div class="class-information collapse">
<div class="full-width">
{{class.longDescription}}
</div>
</div>
</div>
he has supplied some javascript to handle the click on class-overview
$('.class-overview a').on('click',function(e) {
e.preventDefault();
});
$('.class-overview').on('click',function() {
$('.class-overview.active').removeClass('active').next('.class-information').collapse('hide');
$(this).addClass('active').next('.class-information').collapse('show');//.css('top',offset).collapse('show');
});
and i have a line like this in my state provider
// default route
$urlrouterProvider.otherwise("/")
So the problem is that the ui-router handles the click and sends me back to the home page.
The ideal solution is to leave as much of his markup intact, so can anyone tell me how I stop ui-router handling the click?
or failing that, how I might use ng-click and ng-show to get the same effect, i.e. hiding and showing the class-information div...
If I understand well your question, you want to display the .class-information div when you click on the .class-overview element.
That can be done by using a variable in a ng-show like this:
<div ng-repeat="class in vm.classes">
<div class="class-overview">
<a href="#" ng-click="display = !display">
<span class="class-title">{{class.description}}</span>
... more stuff here
</a>
</div>
<div class="class-information" ng-show="display">
<div class="full-width">
{{class.longDescription}}
</div>
</div>
</div>
The display variable will be falsy when you land on the page, therefore the ng-click will be executed, this variable will be set to true.
I see that you are using a collapse class to hide the content if it is collapsed. Then you could use angular ng-class to put the collapse class when the display variable is false. Your div.class-information would look like this:
<div class="class-information" ng-class="{collapse: !display}">
<div class="full-width">
{{class.longDescription}}
</div>
</div>

AngularJS: Infinite Scroll in a container

I'm using ngInfiniteScroll in AngularJS for a specific container.
Simple example
html
<div id="containerInfiniteScroll" class="container">
<div infinite-scroll="next()" infinite-scroll-disabled="disabled" infinite-scroll-distance="1" infinite-scroll-container='"#containerInfiniteScroll"'>
<div class="row">
<div class="col-xs-12 col-sm-12 col-lg-12">
<span data-ng-repeat="el in elements | limitTo:limit">
{{el}}
</span>
</div>
</div>
</div>
</div>
css
.container{overflow-y:scroll;}
js
//Varibles...
$scope.elements = ['element1','element2','element3','...','elementn'];
$scope.limit=50;
$scope.disabled = false;
//Function that infinte-scroll calls:
$scope.next = function(){
$scope.limit=$scope.limit+50;
$scope.disabled = $scope.limit>=elements.length;
};
The ngInfiniteScroll works as expected for contentInfiniteScroll content. Except this case...
Don't charge more elements if the scroll-y of the page (not the
scroll of the container) is on bottom.
And only don't work in this case...
What am I doing wrong? It's me, or maybe I need to retouch the library ngInfiniteScroll.js ?
Thank you.

My directive stopped working when I started using ng-repeat with a different controller. What am I doing wrong?

So I followed this guide so I could have a nav bar on every page: http://tomaszdziurko.pl/2013/02/twitter-bootstrap-navbar-angularjs-component/
And it was working, until I created a separate controller to populate my bootstrap carousel. The thing is, my ng-repeat works fine, but when it does I can't see my navbar on that page. I can see it just fine on other pages. I believe this is a scoping issue, but I am not sure where.
This is what I have in the main body of this page:
<body>
<reusable-navbar></reusable-navbar>
<!-- Carousel Start -->
<div id="main-carousel" class="carousel slide container" data-ride="carousel">
<!-- Wrapper for slides -->
<div class="carousel-inner">
<!--Must set this by hand-->
<div class="item active">
<img alt="" src="../Revamp/Images/carousel/1.jpg">
</div>
<!--Repeat through the rest-->
<div ng-controller="carouselPhotoController">
<div class="item" ng-repeat="source in source">
<img alt="" ng-src="{{source.source}}">
</div>
</div>
</div>
</div>
And my controller looks like this:
var carouselPhotoController=angular.module("revampApp", []);
carouselPhotoController.controller("carouselPhotoController", function($scope, $http){
$http.get('../Revamp/Images/carousel/photos.json').success(function(photos){
//Carousel photos
$scope.source = photos;
})
});
And the directive is identical to the one in that walk through, just with a different template. So how to I get it so my nav bar will show up AND I can use ng-repeat?
Make sure you are not recreating the app.
This creates a new app:
var carouselPhotoController=angular.module("revampApp", []);
But this only accesses an app already created (note the absence of the second parameter):
var carouselPhotoController=angular.module("revampApp");
Change the above line and it should work.

Implement div group hide / show

I'm trying to implement a mechanism to hide / show div within a group meaning that I want to only have one div displayed for the group. Of course, I can implement this using the ng-show directive but I would like to have something more generic.
For example:
<div div-group="mygroup">
<div id="div1"> ... </div>
<div id="div2"> ... </div>
<div id="div3"> ... </div>
</div>
If I call a function like showDiv("div1"), other div would be hidden (div2 and div3). I thought about adding an object on the root scope containing all inner div status (displayed or hidden).
Thanks very much for your help!
Thierry
One way could be using ng-class
.show{
display:block
}
.hide{
display:none;
}
<div div-group="mygroup">
<div id="div1" ng-class={true:'show',false:'hide'}[selecteddiv='div1']> ... </div>
<div id="div2" ng-class={true:'show',false:'hide'}[selecteddiv='div2']> ... </div>
<div id="div3" ng-class={true:'show',false:'hide'}[selecteddiv='div3']> ... </div>
</div>
$scope.choose=function(id){
$scope.selecteddiv=id;
}
So you could pass proper id as string in function
If you want to do this in vanilla js, you just can write your showDiv function like this
function showDiv(id) {
//hide all divs
document.findElementById('div1').style.display = 'none';
document.findElementById('div2').style.display = 'none';
document.findElementById('div3').style.display = 'none';
//show just the div you want
document.findElementById(id).style.display = 'block'; //or whatever it was before
}

Resources