This is html:
<div class="main" ng-controller="companies">
<ul style="list-style: none;">
<li ng-repeat="company in companies | orderBy:'name' | filter:companies_filter">
<a href="#!/companies/{{company.id}}" ng-click="companySelected(company)">
{{company.name}}
</a>
</li>
</ul>
</div>
This is router.js:
...
.when('/companies', {
templateUrl: '../html/companies.html',
controller: 'companies'
})
.when('/companies/:companyId', {
templateUrl: '../html/companies.html'
});
....
This is working:
$scope.companySelected = function(company) {
console.log(company);
}
The list is pretty huge. If I scroll down to bottom and click on a company, the list gets back to the top. Same happens when I enter company name to filter and click, again, it gets back to its initial view. How can I fix it so that it stays where it is?
you are looking for $anchorScroll.
Here is the documentation: anchorScroll
When you return from company page to the list, you might need to add hash Id of the element that you clicked earlier. To that, you first need to add ID to li element like below.
<li id={{company.id}} ng-repeat="company in companies | orderBy:'name' | filter:companies_filter">
<a href="#!/companies/{{company.id}}" ng-click="companySelected(company)">
{{company.name}}
</a>
</li>
while coming back to list page, call $anchorScroll(); You can send hash as a parameter in the anchorscroll or update the location hash like below before calling $anchorScroll();
$location.hash('id_value');
Related
Please see attached image.
So, this is a navbar, where clicking on any of the tabs, the content below the nav will change based on which tab is selected.
Here is code for an example of how the function works for when click event is handled when Bespoke Manufacturing is clicked:
toggleBespoke() {
this.showBespoke = true;
this.showHow = false;
this.showCasting = false;
this.showForging = false;
this.showInjection = false;
this.showPressing = false;
this.showTurning = false;
}
As you can see, it's simple, the about showAbout variable is set to true whilst all the others are manually set to false.
ngClass is used which gives the blue highlighted effect so only one of the tabs gets highlighted when selected as only one can be true whilst others are false.
The same logic is then applied to the 6 other tabs.
But this is my question, what is the way to prevent code duplication and handle such click events in one function only?
You can use a property in your component which stores the active tab.
export class AlarmsComponent {
activeTab = 'bespoke';
}
And then you can use it in your template like this:
<ul class="tabs">
<li>
<a (click)="activeTab = 'bespoke'"
[ngClass]="{active: activeTab === 'bespoke'}"
>
Bespoke manufacturing
</a>
</li>
<li>
<a (click)="activeTab = 'how'"
[ngClass]="{active: activeTab === 'how'}"
>
How it works
</a>
</li>
</ul>
<div class="tab-bespoke" *ngIf="activeTab === 'bespoke'">
Bespoke manufacturing tab content
</div>
<div class="tab-how" *ngIf="activeTab === 'how'">
How it works tab content
</div>
Another solution (dirtier than the previous one) is to create a single function executed on click passing the $event param.
For instance you have something like the following to manage the nav item:
<li class="nav-item"><a id="bespoke" class="nav-link" href="#" tabindex="-1" (click)="onClick($event)">BESPOKE MANUFACTURING</a> </li>
And inside the class you have onClick function as the following:
onClick(ev){
let clickedId = ev.target.id;
//clickedId is the id of the clicked element and you can add class accordingly
if(clickedId === 'bespoke'){
//the user clicked the bespoke item
}
}
Create a variable to store the current active tab, in interaction with tab set current clicked tab to activeTab, then apply active inactive class using this variable ,for example in
app.component.ts
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
activeTab: string = 'tab1';
isActive(tabName){
return this.activeTab === tabName;
}
makeActive(tab){
this.activeTab = tab;
}
}
in Html
app.component.html
<ul class="nav nav-tabs">
<li class="nav-item">
<a [ngClass]="{'nav-link': true, 'active':isActive('tab1')}" (click)="makeActive('tab1')">Tab 1</a>
</li>
<li class="nav-item">
<a [ngClass]="{'nav-link': true, 'active':isActive('tab2')}" (click)="makeActive('tab2')">Tab 2</a>
</li>
<li class="nav-item">
<a [ngClass]="{'nav-link': true, 'active':isActive('tab3')}" (click)="makeActive('tab3')">Tab 3</a>
</li>
<li class="nav-item">
<a [ngClass]="{'nav-link': true, 'active':isActive('tab4')}" (click)="makeActive('tab4')">Tab 4</a>
</li>
</ul>
<div>
<div class="tab-bespoke" *ngIf="isActive('tab1')">
tab 1 content
</div>
<div class="tab-bespoke" *ngIf="isActive('tab2')">
tab 2 content
</div>
<div class="tab-bespoke" *ngIf="isActive('tab3')">
tab 3 content
</div>
<div class="tab-bespoke" *ngIf="isActive('tab4')">
tab 4 content
</div>
</div>
here is the running sample https://stackblitz.com/edit/angular-bootstrap-4-starter-cbrzm5
I'd go with the framework and actually use proper routing instead of hiding content by hand.
as a bonus, you'll get the current tab get set to automatically when the user refresh the page.
I have the following HTML markup:-
<ul class="dropdown-menu">
<li ng-repeat="track in songTracks track by $index" ng-class="{active: $index===selectedIndex}">
<a ng-click="setSongTrack(track)">
{{track.name}}
</a>
</li>
</ul>
In Angular, I use this:-
$scope.setSongTrack = setSongTrack;
$scope.selectedIndex = 0;
$scope.index= function(i) {
$scope.selectedIndex=i;
};
Which passes to DASHJS like so:-
function setSongTrack(track) {
musicPlayer.setSongTrack(track);
$scope.selectedIndex = track;
}
musicPlayer being the instance of DASHJS, which parses my audio tracks. This all works - I get a bunch of list elements with the track names, I can click them and independently load each track as expected. However, I need to add some visual way of informing the user which track is currently selected, which is probably best done by way of adding a CSS 'active' class.
Currently this just sets an 'active' class to the first list element, regardless if I click another and it changes the track. If I click another list item, the 'active' class is removed from the first list item. I need the 'active' class to only be on the currently selected track, i.e. the selected list item.
Now I'm stumped. I've read over a lot of similar SO questions and haven't located something which I can get to work.
Can anyone enlighten me? Fairly new to Angular and so, I'm still a bit of a noob with it.
Instead of using the index of the track to mark it as selected, use the track itself. Write a function like trackSelected() which takes a track, and use that to apply a class with ngClass. Eg...
HTML
<ul class="dropdown-menu">
<li ng-repeat="track in songTracks track by $index" ng-class="{ 'active': isSelected(track)} ">
<a ng-click="setSongTrack(track)" ng-bind="track.name">
</a>
</li>
</ul>
JS
$scope.selectedTrack = null;
$scope.setSongTrack = function(track) {
musicPlayer.setSongTrack(track);
$scope.selectedTrack = track;
}
$scope.isSelected = function(track) {
return $scope.selectedTrack === track;
}
This is because you may add or remove tracks from the list in your view at some point, and then the selected index will be invalid.
just go like this:
<ul class="dropdown-menu">
<li ng-repeat="track in songTracks track by $index" ng-class="{active: track===selectedTrack}">
<a ng-click="setSongTrack(track)">
{{track.name}}
</a>
</li>
</ul>
and in your js
$scope.selectedTrack = undefined;
$scope.setSongTrack = function (track) {
musicPlayer.setSongTrack(track);
$scope.selectedTrack = track;
}
This way you can also do stuff like presenting the selected track in your player like below just with one click, no need to fiddle with indexes and such.
<h1>{{selectedTrack.name}}</h1>
<p>
<span>{{selectedTrack.totalTime}}</span>
</p>
Here is a simple fiddle
Alternate Solution
Going the $index way should be like this(Just explaining to help you see what you did wrong)
<ul class="dropdown-menu">
<li ng-repeat="track in songTracks track by $index" ng-class="{active: $index===selectedIndex}">
<a ng-click="setSongTrackIndex($index)">
{{track.name}}
</a>
</li>
</ul>
Notice that i chanced setSongTrack(track) to setSongTrackIndex($index). Then you would of course change your js accordingly, like so:
function setSongTrackIndex(trackIndex) {
musicPlayer.setSongTrack(songTracks[trackIndex]);
$scope.selectedIndex = trackIndex;
}
$scope.setSongTrack = setSongTrack;
$scope.selectedIndex = 0;
If you need further explanation please comment.
I am new to AngularJS. I want to create links for elements in the array. Suggest me some code or descriptives.
app.js
app.controller('ItemsController', function($scope) {
$scope.message = 'Items are displayed';
$scope.items=['Appliances','Books','Cosmetics','Home & Furniture','Mens','women','kids'];
});
items.html
<div ng-controller="ItemsController">
<p><h1>Type a letter in the input field:</h1></p>
<p><input type="text" ng-model="test"></p>
{{message}}
<ul><h2>
<li ng-repeat="x in items| filter:test">
{{ x }}
</li>
</h2></ul>
</div>
Here is the sample output for the above code. Items displayed in the output should be links.
If you are using routing then just create an anchor tag in the li.
<ul>
<h2>
<li ng-repeat="x in items| filter:test">
{{x}}
</li>
</h2>
</ul>
In the route configuration, just handle the specific route pertaining to the item. Like if you want to display the details for the item.
myApp.config(['$routeProvider',function($routeProvider){
$routeProvider.
when('/details/:item',{
templateUrl:'partials/details.html',
controller: 'DetailsController'
}).
otherwise({
redirectTo : 'yourdefaultpath'
})
Item shall be available in your controller as a routeParam and then you can display your data accordingly.
Hope this helps.
You can use ui-sref and declare your items as states using ngRoute in your app, this is how would be your code:
<ul>
<li ng-repeat="x in items| filter:test">
<a ui-sref="{{x}}"><h2>{{ x }}</h2></a>
</li>
</ul>
Update your JS to add the states, like this:
app.config(function($stateProvider) {
$stateProvider.state('Appliances', {
url: 'Appliances',
templateUrl: 'app/views/appliances.html'
});
});
I am trying to use mmenu for an application and everything is going great, except the menu title.
As for now, the menu has 1 parent-item, 'Menu A', and the menu hierarchy is the following:
Menu A
A1
A1a
A2
A2a
A2b
First level ('Menu A') slides into Second level (new menu with 'A1' and 'A2'). Third level is vertical and is expanded under its parent. More graphical detail comes up next.
First step (sorry, I'm only allowed to post 2 links, so I removed this screenshot): The initial state of the menu is the parent item A, with no title. OK
Second step: When I click the parent, the title changes to 'Menu A' and the children are loaded into the menu. OK
Third step: Lastly, when I click on a child loaded from the previous step, the title disappears, and I have no clue why. NOT OK Any ideas?
Here follows the javascript and HTML code of my menu:
$("#navigation").mmenu(
{ // options
"extensions": [
"border-full",
"multiline",
"iconbar"
],
navbar: {
title: ''
},
navbars: [
{
position : 'top',
content : ['prev','title']
}
],
},
{ // configuration
offCanvas: {
pageSelector: "#wrapper",
wrapPageIfNeeded: false
}
}
);
<nav class="sort-pages modify-pages" id="navigation">
<ul>
<li class="">
<span>
<span>Menu A</span>
</span>
<ul class="child-menu" id="sub-0">
<li class="">
<span>
<span>A1</span>
</span>
<ul class="child-menu mm-vertical" id="sub-sub-1">
<li class="MenuTransactionLink">
<a href="#" link="a1a">
<span>A1a</span>
</a>
</li>
</ul>
</li>
<li class="">
<span>
<span>A2</span>
</span>
<ul class="child-menu mm-vertical" id="sub-sub-2">
<li class="MenuTransactionLink">
<a href="#" link="a2a">
<span>A2a</span>
</a>
</li>
<li class="MenuTransactionLink">
<a href="#" link="a2b">
<span>A2b</span>
</a>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</nav>
I am happy to help with more details, if needed.
Following the developer's Github issue list, i found this in particular:
I think this is because "vertical" panels (all panels when you use slidingSubmenus: false) do not have a navbar. I'll see if I can do something about that.
For now, I guess it's either slidingSubmenus or no title.
(source: github.com/FrDH/jQuery.mmenu/issues/535)
I even tried forcing it using Javascript, by binding an 'on click' event to the menu items:
$('nav a.mm-fullsubopen').on('click', function (e){
var title = $('#navigation').find('.mm-title')[0];
title.text = $('#' + $('.mm-opened.mm-panel.mm-highest').attr('id')).find('.mm-title').text();
$(title).removeClass('mm-hidden')
});
This works for a few miliseconds, until the mmenu plugin overwrites the title with an empty string (see link from third step above). It must have some kind of event that fires after this one, making mine completely useless.
How can I do a workaround to make this behaviour possible?
Problem:
I have an unordered list of items which are returned from a json call and are output using ng-repeat. Each one of these items has a class name (there are about 9 categories).
I have a second unordered list which is simply a list of available categories.
Aim:
I want to be able to select one of the categories in the right hand list, which will apply a filter to the actual list of returned elements. This should be activated via a toggle (so click once: filtered, click again: filter removed). So it is simply looking to match the classname in the clicked element, to the elements that share the same classname in the list of json data.
I cannot use ng-model (as this is reserved for certain form elements).
For my jsfiddle I am simply using static html.
Here is my angular code:
/* angular custom filter on returned ajax api data */
var app = angular.module('app', []);
app.controller('main', function($scope) {
$scope.chFilters = {};
$scope.links = [
{name: 'atm'},
{name: 'internet'},
{name: 'mobile'},
{name: 'sms'},
{name: 'postal'}
];
$scope.channels = ["ATM", "INTERNET", "SMS", "POSTAL","MOBILE"];
});
(this is based on another question I found on SO). Unfortunately the fiddle is a bit messy and has some extraneous code in it.
HTML:
<div ng-app="app">
<div ng-controller="main">
<ul>
<li class="atm">Some stuff ATM</li>
<li class="internet">Some stuff INTERNET</li>
<li class="sms">Some stuff ATM</li>
<li class="atm">Some stuff ATM</li>
<li class="postal">Some stuff POSTAL</li>
<li class="atm">Some stuff ATM</li>
<li class="internet">Some stuff INTERNET</li>
<li class="postal">Some stuff POSTAL</li>
<li class="postal">Some stuff POSTAL</li>
<li class="atm">Some stuff ATM</li>
<li class="sms">Some stuff SMS</li>
<li class="mobile">Some stuff MOBILE</li>
<li class="internet">Some stuff INTERNET</li>
<li class="mobile">Some stuff MOBILE</li>
</ul>
<ul class="channel-filters">
<li ng-repeat="link in links | filter:chFilters" class="{{link.name | lowercase}}"><a ng-click="chFilters.name = link.name">{{link.name | uppercase}}</a></li>
<li class="last" ng-click="chFilters.name = ''">CLEAR FILTER</li>
</ul>
<ul>
<li ng-repeat="channel in channels | filter:chFilters">
<strong>{{channel}}</strong>
<a ng-click="chFilters = channel">{{channel}}</a>
</li>
</ul>
<!-- original -->
<ul>
<li ng-repeat="link in links | filter:chFilters">
<strong>{{link.name}}</strong>
<a ng-click="chFilters.name = link.name">{{link.name}}</a>
</li>
</ul>
</div>
</div>
This is the actual HTML from the application (with the call to the api).
<ul class="accordion">
<li class="search-text-channel">
<input type="textarea" ng-model="searchTextChannel.$" placeholder="Search"/>
</li>
<li ng-repeat="day in interactions.model.byDay | filter:searchTextChannel" ng-click="hidden = !hidden" ng-init="hidden = false" class="{{day.channel | removeSpace | lowercase}}" ng-class="{'closed': !hidden, 'open': hidden}">
<span class="date">{{day.date}}</span>
<span class="subheading">{{day.channel}}</span>
<ul ng-show="hidden">
<li ng-repeat="interaction in day.interactions">
{{interaction.time}} {{interaction.description | removeUnderscore}}
</li>
</ul>
</li>
<li class="load-more">
<i class="fa fa-plus"></i>LOAD MORE
</li>
</ul>
I have managed to recreate this functionality in jquery, but I think it would be better to implement an angular solution in an angular application.
I've tried researching and also attempted to implement show/hide as well as a custom filter, but so far no joy.
Here is my (messy) jsfiddle
<ul>
<li ng-repeat="channel in channels | filter:chFilters.name">
<strong>{{channel}}</strong>
<a ng-click="chFilters = channel">{{channel}}</a>
</li>
</ul>
<!-- original -->
<ul>
<li ng-repeat="link in links | filter:chFilters.name">
<strong>{{link.name}}</strong>
<a ng-click="chFilters.name = link.name">{{link.name}}</a>
</li>
</ul>
Update Plunker
Let me know if you have any question on this.
Here is part of the solution:
Suppose your items look like this:
$scope.items = [
{ class: "atm", label: "Some stuff ATM" },
{ class: "internet", label: "Some stuff INTERNET" },
{ class: "sms", label: "Some stuff SMS" },
{ class: "postal", label: "Some stuff POSTAL" },
...
];
To show a filtered list (only filtering by a single channel for now): create a separate list in the scope, with the filters applied:
$scope.click = function(name) {
$scope.chFilters.name = name;
$scope.filteredItems = $scope.items.filter(function(item) {
return item.class === $scope.chFilters.name;
});
};
Call this click handler from the bottom list:
...<a ng-click="click(link.name)">{{link.name | uppercase}}</a>....
And show filteredItems in the top list:
<ul>
<li ng-repeat="item in filteredItems" ng-class="item.class">{{item.label}}</li>
</ul>
So this is really just a starting point, it should be extended to handle multiple filters, etc...