I have a Bootstrap implementation where the navigation bar is an unordered list with each items set to highlight when active. The highlight itself is styled using CSS but the check for active status is made using AngularJS:
<ul class="nav navbar-nav navbar-right">
<li ng-class="{ active: isActive('/blog')}">
<a onClick="pagetop()" href="#blog">BLOG</a>
</li>
</ul>
The isActive() method is defined in an AngularJS Controller as:
function HeaderController($scope, $location){
$scope.isActive = function (viewLocation) {
return viewLocation === $location.path();
};
}
As you can see, AngularJS simply adds an .active class to the <li> item if the linked URL is active. This is the class that is styled using CSS. This works fine when the currently open page is http://localhost:8888/a-s/#/blog/ but not when it's http://localhost:8888/a-s/#/blog/sub-page/ or http://localhost:8888/a-s/#/blog/sub-page/sub-sub-page/. How can I modify the code to ensure all paths under /blog trigger the .active class logic? Is there any way one could use wild-cards in this syntax?
Now you checking whether path and the passed value are equal, instead you can check whether the passed value exists in the path
function HeaderController($scope, $location) {
$scope.isActive = function(viewLocation) {
return $location.path().indexOf(viewLocation) > -1;
};
}
This is not clean solution. Because it work on http://localhost:8888/a-s/#/blog-another/ b or http://localhost:8888/a-s/#/blog_edit/ b and so on. It is need to update like this:
function HeaderController($scope, $location) {
$scope.isActive = function(viewLocation) {
var path = $location.path();
var addViewLocation = viewLocation+"/";
var inPath = false;
if(path.indexOf(addViewLocation)==-1)
{
inPath = path.indexOf(viewLocation)+viewLocation.length==path.length;
}
else
inPath = true;
return inPath;
};
}
This is test locations:
isActive("www.location.ru/path","/path");
true
isActive("www.location.ru/path/tr","/path");
true
isActive("www.location.ru/path-r/tr","/path");
false
isActive("www.location.ru/path/","/path");
true
isActive("www.location.ru/path/we/ew","/path");
true
isActive("www.location.ru/path-another/we/ew","/path");
false
Related
I built tabbing functionality in my web app using Links and an Outlet with subroutes
const { currentTab } = useLoaderData();
function tabClassName(tab: string) {
if (currentTab == tab) { return "is-active"; }
return "";
}
return ([
<div className="tabs is-centered m-5 pr-5 pl-5">
<ul>
<li className={tabClassName("tab1")}><Link to="tab1">one</Link></li>
<li className={tabClassName("tab2")}><Link to="tab2">two</Link></li>
<li className={tabClassName("tab3")}><Link to="tab3">three</Link></li>
</ul>
</div>,
<Outlet></Outlet>
]);
The loader function supplies the component with the current subroute like this:
const parts = new URL(request.url).pathname.split("/");
const tab = parts.pop() || parts.pop() || ""; // get final part of path /settings/tab1 -> tab1
if (tab === "settings") {
return redirect("/settings/tab1");
}
return json<LoaderData>({ currentTab: tab });
When I click the links the correct route is loaded instantly. However, the css class is only applied after clicking the same link a second time. I found out that the loader function is only executed after the second click. How can i force remix to make a server request again? or should i use client side js for this? I cant use the NavLink component because of the way my css framework is structured.
Thanks!
I have a small unordered list in a vue template:
<ul style="border-bottom:none !important; text-decoration:none">
<li class="commentToggle" v-bind:class="{active:commentActive}" v-on:click="setInputName('new')">New Comment</li>
<li class="commentToggle" v-bind:class="{active:commentActive}" v-on:click="setInputName('note')">Note</li>
</ul>
and I then have a data variable for commentActive and a function I call to set the input name:
data () {
return {
commentActive: false,
}
},
methods: {
setInputName(str) {
this.inputName = str;
this.commentActive = true;
},
}
And this is functional but it is obviously setting BOTH inputs to active when I click on one of them. How do I alter this to only set the clicked line item active?
You need to use a unique identifier to determine which comment is active. In the most rudimentary way using your current setup:
<li class="commentToggle"
v-bind:class="{active:commentActive === 'new'}"
v-on:click="setInputName('new')">
New Comment
</li>
<li class="commentToggle"
v-bind:class="{active:commentActive === 'note'}"
v-on:click="setInputName('note')">
Note
</li>
setInputName(str) {
this.inputName = str;
this.commentActive = str;
},
The adjustment that we made is to ensure that the commentActive matches the str that we've used. You can change that identifier to anything else, and pass additional arguments if you so choose.
I am using node.js, express and express-handlebars. I have a menu in the top of my layout.
In my server.js, I set all the pages with
app.locals.pages = [
{ title: 'Home', link: '/' },
{ title: 'About', link: '/about' },
{ title: 'Contact', link: '/contact' },
];
and in the layout file, I print the pages with
{{#each pages}}
<li class="nav-item">
<a class="nav-link" href="{{link}}">{{title}}</a>
</li>
{{/each}}
but I want to add a class to the menu item which is currently being active.
How can I save which of the 3 menu items is active?
I solved it without to pass params over the router request. Just simple created a js file to nav partial, pass the active path of the location to the nav item and add an active class.
(function($) {
$(document).ready(function() {
// get current URL path and assign 'active' class
var pathname = window.location.pathname;
$('.nav.navbar-nav > li > a[href="'+pathname+'"]').parent().addClass('active');
});
})(jQuery);
You can pass a body_id (or any other name) parameter from your routes to your views e.g.
router.get('/', function( req, res ){
var pageInfo = {};
pageInfo.body_id = 'homepage';
// assign more parameters relevant for this view here
res.render('home_page', pageInfo );
});
Now, in your view file, assign the body_id as the id of body i.e.
body(id= typeof body_id === "undefined" ? "body_id" : body_id)
Now, you can manage the active links in your css as
body#home_page .nav_item:nth-child(1),
body#about_page .nav_item:nth-child(2),
{
background-color: red;
/* more css styles that you want to give to your active menu item */
}
i don't test this for your code, but thing it should work, but if you use handlebars, you can try this
Ember.Handlebars.helper('isSelected', function(v1,v2, options) {
if (v1 && v2) {
return new Ember.Handlebars.SafeString("active");
}else{
return new Ember.Handlebars.SafeString("nav-item");
}
});
and on your codes
on your node js
router.get('/', function( req, res ){
var pageData = {};
pageData.page_link = 'home';
res.render('template_page', pageData );
});
on your template
{{#each page in pages}}
<li class="{{isSelected page.link page_link }}">
<a class="nav-link" href="{{page.link}}">{{page.title}}</a>
</li>
{{/each}}
again: i don't test this. but handlebars helper work fine for html elements
Here in first condition i was able to disbale all parents except current parent that is selected.
checkbox.js
if (geoLocation.id === 5657){
var getParent = geoLocation.parent();
$.each(geoLocation.parent(),function(index,location) {
if (location.id !== geoLocation.id) {
var disableItemId = 'disabled' + location.id;
// Get **strong text**the model
var model = $parse(disableItemId);
// Assigns a value to it
model.assign($scope, true);
}
}
);
At this point i am trying to disbale all the child for the parents that are disabled in above condition. How to achieve that task with below code any help will be appreciated.
So far tried code...
$.each(geoLocation.parent().items,function(index,location) {
if(location.id !== geoLocation.id){
var disableItemId = 'disabled' + location.children.data;
// Get the model
var model = $parse(disableItemId);
// Assigns a value to it
model.assign($scope, true);
}
});
console.log(getParent);
}
If you plan to use angular, better express all of this in a model structure, and use ng-click and ng-disabled to achieve what you need.
Template:
<ul>
<li ng-repeat="item in checkboxes">
<input type="checkbox" ng-disabled="item.disabled" ng-click="disableOthers(item)"> {{item.name}}
</li>
</ul>
Controller:
$scope.disableOthers = function (item) {
// iterate over parents and childs and mark disabled to true
};
I have a controller that fetches images using service and builds up DOM as following,
<ul>
<li ng-repeat="image in images">
<img parentimageclick="{{ image.standard_resolution.url }}" ng-src={{image.thumbnail.url}} />
</li>
</ul>
Now using directive I'm calling a service via same controller
Products.directive("parentimageclick",function(){
var parentimageclick = function(scope,element,attrs) {
element.bind('click',function(){
scope.callChild(attrs.src);
});
};
return parentimageclick;
});
As you see callChild for controller is called which calls service
$scope.callChild = function(data) {
packImage.prepareSend(data);
}
Service code is,
Products.factory('packImage',function($rootScope){
var packImage = {};
packImage.img = "";
packImage.prepareSend = function(img) {
this.img = img;
this.Send();
}
packImage.Send = function(){
$rootScope.$broadcast('takeimage');
}
return packImage;
});
So basically i use this service to access the clicked image in another controller as follows,
Products.controller('PackCtrl',function($scope,packImage){
var images = [];
$scope.$on('takeimage', function() {
//console.log(packImage.img);
var data = {
url: packImage.img,
};
images.push(data);
$scope.images = images;
$scope.$apply();
});
});
Now problem with the above code is it does populate the dom as model updates but what I'm really trying to achieve is different. cuz my DOM remain same.
For eg, i have a hardcode list in my dom as follows,
<ul>
<li></li>
<li></li>
//Ten of them
</ul>
So i couldn't change the above structure and i need some functionality where if the image is fetched in the controller it needs to find the empty list item and append it there. This has to be done until list is finished.
Update:
This http://jsfiddle.net/CQzu5/ should be about rough demo of what i want to do? hope there is an angular way to do same.