Possible to collapse and expand in ractivejs? - ractivejs

Looking at example
HTML
<ul>
<li>Blueberry</li>
<li>Raspberry</li>
<li>Pear</li>
<li>Apple</li>
<li>Banana</li>
<li>Cherry</li>
<li>Mango</li>
<li>Soursop</li>
</ul>
Thinking whether it is possible this way in ractivejs
Like this:
<ul>
{{#each fruit:i}}
{{#if count > 4}}
Show less
{{else}}
<li>{{fruit.name}}</li>
Show more
{{/if}}
{{/each}}
</ul>
If got more items, it would limit only 4 items and display "Show more". Then click on show less after display all item and it will collapse.
Wonder if that is possible? Also would css animation work for class: more and less instead of on-click handlers to expand and collapse? (similar to slide up and down).

Yes, it is possible.
Collapse and expand is merely tracking who is active, and adding the proper display style to those who are. That can be done with plain templating (see upper half of code).
The "show more" can be done in 2 ways:
If you load your data dynamically (via ajax) per load more, then you can just concat to your existing fruits array and use that for iteration.
If your data is already there (you loaded all fruits, yet only want to show a few at a time), you can use a property to track the number of currently displayed, and a computed property to slice the fruits array using that number. Clicking load more will increase that number (say 4 to 8), kicking the computed prop to recalculate using 8 this time.
Here's an example:
<ul>
{{#each slicedFruits:index}}
<li>
<!-- Here, clicking the header sets the current fruit to display -->
<h4 on-click="set('currentFruitIndex', index)">{{name}}</h4>
<!-- Here, we conditionally apply block or none depending on who's showing -->
<div style="display:{{if currentFruitIndex === index}}block{{else}}none{{/if}};">{{details}}
</li>
{{/each}}
<!-- using the built-in `add`, we add 4 to `displayedFruits` -->
<a href="#" on-click="add('displayedFruits', 4)>Load More</a>
</ul>
<script>
component.exports = {
data : {
fruits: [], // load up your fruits here
currentFruitIndex: -1, // change this to any default, -1 for nothing
displayedFruits: 4, // change to how many you liked displayed first
},
computed: {
// We slice the main array `fruits` by `displayedFruits`.
// Changing `displayedFruits` will recalculate this and update the template
slicedFruits: function(){
return this.get('fruits').slice(0, this.get('displayedFruits'));
}
}
};
</script>
As for animation, you can check out Transistions. There's a good example of accordion here.

Related

Displaying Play/Pause Button in angular

I want to implement the show/hide feature of the play and pause buttons on a list of tracks in angular 7. I originally got this feature partially working using angular animation, but my method would change the state of all buttons in my list instead of a single item. I've also tried using ngClass but couldn't seem to get it working right.
Below is my latest efforts. Please help me.
<mat-card class="track-box" *ngFor="let track of tracks" cdkDrag>
<div class="custom-placeholder" *cdkDragPlaceholder></div>
<span>
<mat-icon
class="play-button md-48"
[ngClass]="{'show' : track === selectedTrack}"
(click)="toggle(track)"
> play_circle_outline</mat-icon>
<mat-icon
class="pause-button md-48"
[class.selected2]="track === selectedTrack"
(click)="toggle(track)"
>pause_circle_outline</mat-icon>
</span>
I would have to disagree with #Tomato 's answer, as per my comment on his answer.
However, what I would recommend is using Angular's NgClass which is similar to ngIf. But here's the catch.
When it comes to ngIf, you're creating 2 components, hiding one and showing the other, and doing that operation over and over again. Which is not the best code practice.
However, in the case of ngClass, you're creating one component and 2 separate SCSS classes for the same component. Whenever a certain condition is satisfied, one SCSS Class gets applied, and so forth.
HTML File
<span>
<button
class="play-pause-button"
[ngClass]="toggle(track)"></button>
</span>
TS File
export class Component {
let isPlay = false;
public toggle(track: string) {
this.isPlay = !this.isPlay;
this.isPlay ? return 'play-button'; : return 'pause-button'
}
}
That way, the toggle() function will return a SCSS class name based on the current status of the button itself.
Using ngClass
You could change the shown button icon by using ngClass as following:
<mat-card class="track-box" *ngFor="let track of tracks" cdkDrag>
<div class="custom-placeholder" *cdkDragPlaceholder></div>
<span>
<mat-icon
class="md-48"
[ngClass]="{'play-button' : track != selectedTrack,
'pause-button' : track === selectedTrack}"
(click)="toggle(track)">Some output here</mat-icon>
</span>
</mat-card>
Using ngIf
You could maybe use an ngIf (docs) to show/hide the buttons, though as Chris pointed out, this creates two elements for every track in your list and could cause performance issues on a very huge list.
<mat-card class="track-box" *ngFor="let track of tracks" cdkDrag>
<div class="custom-placeholder" *cdkDragPlaceholder></div>
<span>
<mat-icon
class="play-button md-48"
*ngIf="track != selectedTrack"
(click)="toggle(track)">play_circle_outline</mat-icon>
<mat-icon
class="pause-button md-48"
*ngIf="track === selectedTrack"
(click)="toggle(track)">pause_circle_outline</mat-icon>
</span>
</mat-card>
When your selectedTrack does not equal the track, show the play-button, if it is the same track, show the pause-button.
You could also use the if-else-block for this (from the docs):
<div *ngIf="condition; else elseBlock">Content to render when condition is true.</div>
<ng-template #elseBlock>Content to render when condition is false.</ng-template>

Adding and Removing Styles to li group using Angular 2

<ul>
<li (click)="AddColor($event)">ONE</li>
<li (click)="AddColor($event)">TWO</li>
<li (click)="AddColor($event)">THREE</li>
</ul>
AddColor(e){
e.srcElement.style.color="blue"
}
I have the above list when i click any one of the li item out of 3, the clicked label color should be changed. when i click another all item colors should be revert back to original and change color of current clicked item.
#Mehdi said, you should not access DOM directly untill there is a need.
Always keep in mind, drive your view with data rather than accessing
DOM directly
I have forked and working snippet https://plnkr.co/edit/fgINMc?p=preview
When using Angular, you don't want to directly manipulate the DOM element. Rather let angular deal with it.
In your example, you can generate your list from an array you declare in the code like so
export class YourClass{
links:any;
activeLink = -1;
//...
constructor(){
this.links = ['ONE','TWO','THREE']
}
//...
}
and then in your template you could have :
<ul>
<li *ngFor="let link of links; let i = index"
(click)="activeLink = i"
[ngClass]="activeLink == i? 'blue' : '' " >
</li>
</ul>
and declare a css class blue :
.blue{
color:blue;
}

Angular 2 - What's the equivalent of routerLinkActive="active" inside a TypeScript (.ts) file?

I was using routerLinkActive="active" inside the html file with routerLink=[myId] to highlight the active anchor in a ul.
Example:
<a [routerLink]="[myId]" class="list-group-item clearfix" routerLinkActive="active">
<div class="pull-left">
<h4 class="list-group-item-heading">{{someName}}</h4>
</div>
</a>
But when I remove the [routerlink]="[myId]" and replace it with a click listener (that does some calculation and then redirects the route using this.router.navigate(['/someURL', myId]) ) the routerLinkActive="active" no longer works/highlights.
Example:
<a (click)="onClick(myId)" class="list-group-item clearfix" routerLinkActive="active">
<div class="pull-left">
<h4 class="list-group-item-heading">{{someName}}</h4>
</div>
</a>
When inspecting the anchor elements using the routerLink=[myId], the style is set to:
.list-group-item.active, .list-group-item.active:focus, .list-group-item.active:hover {
z-index: 2;
color: #fff;
background-color: #337ab7;
border-color: #337ab7;
}
Is it possible to set the active anchor style in the .ts file in the onClick() function? Or is there a simpler way around this?
Highlighting a specific link in the navigation is a reflection of the current state of the app: link A is highlighted because the user is looking at page A.
You're asking how to highlight the link as the result of a click. This approach is less than ideal: you could end up highlighting the link before the new route/page is actually activated. Most importantly, if the user reloads the page or navigates to it directly because they bookmarked it, the link will NEVER be highlighted (because no one clicked it).
You need to find a way to highlight the link based on app state, NOT as the result of an action (the typical flow is: action => changes state => updates UI).
Maybe something like this:
#Component({
template: `
<a [class.active]="currentPath == 'home'">HomeComponent</a>
`
})
export class HomeComponent {
currentPath = '';
constructor(route: ActivatedRoute) {
// Retrieve current path.
this.currentPath = snapshot.url.join('');
}
}
The idea is to retrieve the current path and expose it to the template.
Like Günter suggested, you can probably find more robust code for testing the current path in routerLinkActive's source code.

Targetting Individual Gallery Items - Squarespace Flatiron Template

I am currently using the Flatiron Template in Squarespace 6. Each image in the gallery currently displays the image, a title, and -view- under it. I am looking to change -view- to a different name (a city to be specific) that is unique to each gallery item.
The source code for one of the grid items is this:
<script>
Y.use('squarespace-ui-base', function(Y) {
Y.one(".project-item .meta h1").plug(Y.Squarespace.TextShrink);
});
</script>
<!-- Main Grid -->
<div id="grid" data-collection-id="53ebab59e4b0c8271c405596">
<div class="item">
<a href="/diesel-pop-up-brooklyn-nyc/" data-dynamic-load data-dynamic-receiver="#detail_53ee8134e4b020d5c7faa7b3" >
<div class="wrapper">
<div class="project-title">
<h2>DIESEL POP-UP</h2>
<h3>— view —</h3>
</div>
</div>
<img class="thumbnail loading" data-src="http://static.squarespace.com/static/52937e51e4b006a2894ed2fb/t/540e3941e4b0438c2051340c/1410218366032/2.jpg" data-image="http://static.squarespace.com/static/52937e51e4b006a2894ed2fb/t/540e3941e4b0438c2051340c/1410218366032/2.jpg" data-image-dimensions="480x642" data-image-focal-point="0.5,0.5" alt="2.jpg" data-load="false" />
<noscript><img src="http://static.squarespace.com/static/52937e51e4b006a2894ed2fb/t/540e3941e4b0438c2051340c/1410218366032/2.jpg?format=original"></noscript>
</a>
</div>
I have tried using this in the custom CSS section (just to attempt at targeting one item) but it has only effected the page that the image links to, not the image itself.
.project-item[data-dynamic-href='/diesel-pop-up-brooklyn-nyc/'] {
background-color: red;
}
Is there a code that can target each individual element?
Go to:
Page > Settings > Advanced > Header injection (Index page)
Then paste the following:
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script>
$(document).ready(function () {
$(".item:nth-child(1) h3").text("1st item");
$(".item:nth-child(2) h3").text("2nd item");
$(".item:nth-child(3) h3").text("3rd item");
$(".item:nth-child(4) h3").text("4th item");
});
</script>
Cool. Now you can change the text in the quotations (1st, 2nd, 3rd, 4th item) to whatever text you want to replace 'view'. If you have more than 4 galleries in the index, you can copy a line and paste it below, but just make sure to increase the nth-child item from (4) to (5).
Hope that helps!
Thanks for adding the additional data. Unfortunately you cannot do this. Changes in SquareSpace are global changes. You can make a cosmetic change to all galleries but you cannot target a specific gallery by ID. Squarespace object IDs are dynamic and session based. If you target a specific object ID in your CSS, once you refresh the page the ID will change and the CSS will no longer be valid.
However if there is a scenario where you have individual galleries on separate pages then you can work around the global change by inserting the CSS at the "page" level under settings and not a the site level that calls the object category (not the object ID).
Also changing the content of a label is not a css change. That is an HTML change. In Squarespace you cannot modify/hack the actual HTML in the templated versions.

AngularJs translation directive stop working when I change div class using jquery

Well, being straight forward the problem is my $scope.$apply() is also not digesting the changes to rerun the translate directive.
I show you the HTML with applied translation directive and jQUERY code to change the class on resize of the windows (client).
HTML for menu:
<div id="navigation" ng-cloak>
<div class="container-fluid">
BRAND NAME
<ul class='main-nav'>
<li ng-class="{'active':activeLink == 'home'}">
<a href="#/">
<span>Home</span>
</a>
</li>
<li ng-class="{'active':activeLink == 'planning'}" data-trigger="hover">
<a href="#" data-toggle="dropdown" class='dropdown-toggle'>
<span>Planning</span>
<span class="caret"></span>
</a>
<ul class="dropdown-menu">
<a href="#" data-toggle="dropdown" class='dropdown-toggle'>Goals</a>
<ul class="dropdown-menu">
<li>
{{'TOP_MENU.GOAL_LIST' | translate}}
</li>
</ul>
</li>
</ul>
</li>
</ul>
</div>
</div>
HERE is the JQuery Code to make it responsive for screen size changes.
function checkLeftNav() {
var $w = $(window),
$content = $("#content"),
$left = $("#left");
if ($w.width() <= 840) {
if (!$left.hasClass("mobile-show")) {
$left.hide();
$("#main").css("margin-left", 0);
}
if ($(".toggle-mobile").length == 0) {
$("#navigation .user").after('<i class="fa fa-bars"></i>');
}
if ($(".mobile-nav").length == 0) {
createSubNav();
}
} else {
if (!$left.is(":visible") && !$left.hasClass("forced-hide") && !$("#content").hasClass("nav-hidden")) {
$left.show();
$("#main").css("margin-left", $left.width());
}
$(".toggle-mobile").remove();
$(".mobile-nav").removeClass("open");
if ($content.hasClass("forced-fixed")) {
$content.removeClass("nav-fixed");
$("#navigation").removeClass("navbar-fixed-top");
}
if ($w.width() < 1200) {
if ($("#navigation .container").length > 0) {
versionFluid();
$('body').addClass("forced-fluid");
}
} else {
if ($('body').hasClass("forced-fluid")) {
versionFixed();
}
}
}
}
Now What my solution was to get he nav element scope on which the jquery is applying class and call the $apply() on its scope. which is not working.
Solution Code:
$(window).resize(function(e) {
checkLeftNav();
// get the scope of element and apply $apply()
var sc = angular.element('.mobile-nav').scope();
sc.$apply();
});
But still when ever the screen size is changed to mobile view less than 840 I can see direct code values instead of translated text in menu like this. and When I change back to screen width more than 840 it shows correct translated text. I am experimenting this on chrome on my pc by resizing. I checked on mobile its not translating there too.
AngularJS provides two-way data binding, not two way everything binding. It's not intended to be used this way. $apply() only looks at the data model - it is the function you call when you want to say "Hey, Angular, I've updated the data model, come have a look!" That is literally its only purpose. There is no method in Angular designed to look at the DOM itself for changes - that's very inefficient and against Angular's philosophies anyway, which is why it ships without jQuery.
You might want to evaluate other frameworks that better support this kind of thing. However, if you really wish to do this you can easily convert it into a proper AngularJS module. The best way is to simply paste all of this code into a controller, and then change the window resize binding to use Angular's $window service, like:
var windowElement = angular.element($window);
windowElement.bind('resize', function() {
// Do my calculations here.
});
With your calculations would you compute the same variables but you would store them in scope variables and then adjust your template to use them. For example, what you're doing with $(".toggle-mobile").remove(); could be replaced by:
if (windowElement.width > 840) {
// Other code here
$scope.isMobile = false;
} else {
$scope.isMobile = false;
}
and in your template:
<i class="fa fa-bars"></i>
Give it a whirl, play with it for a week or two, and you'll never go back to jQuery. It takes a lot of getting used to, but once you do you realize how broken the whole "I'm looking at my template and have no idea what mystery event handlers are bound to all this stuff" concept was to begin with.

Resources