I have this global variable for config values that I require in index.js
import React from 'react';
import ReactDOM from 'react-dom';
// Config
const Dir = require('./Config/dir.jsx');
// Components
import Header from './Components/Header.jsx';
ReactDOM.render(<Header />, document.getElementById('app'));
this is the content of my dir.jsx
module.exports = {
css: 'public/css/',
js: 'public/js/',
img: 'public/img/'
}
When I accesssed the config variable in my header.jsx components using { Dir.css } it gives me an error "Dir is not defined".
Here's the header.jsx:
import React from 'react';
require('./../Stylesheets/header.scss');
class Header extends React.Component {
render() {
return (
<div>
<nav className="navbar navbar-default">
<div className="container-fluid">
<div className="navbar-header">
<button type="button" className="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span className="sr-only">Toggle navigation</span>
<span className="icon-bar"></span>
<span className="icon-bar"></span>
<span className="icon-bar"></span>
</button>
<a className="navbar-brand" href="#">{ Dir.img }</a>
</div>
<div className="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul className="nav navbar-nav">
<li className="active">Link <span className="sr-only">(current)</span></li>
<li>Link</li>
</ul>
<ul className="nav navbar-nav navbar-right">
<li>Link</li>
</ul>
</div>
</div>
</nav>
</div>
);
}
}
export default Header;
Here's the screenshot:
Import Dir inside the Header component as
import Dir from './Config/dir.jsx'
I presume you're using webpack to build your project
webpack won't import files unless there're used. In your case, you're just importing Dir and not using anywhere. webpack will skip this import for obvious reason.
There are 2 things you can do:
Import Dir inside the Header component
Pass the Dir as a prop to the Header component. Like this <Header dir={Dir} />. This can be accessed by this.props.dir inside the Header component.
I'll prefer the 1st way of doing.
P.S. You shouldn't use import and require together.
Related
My Vue component navbar.vue does not import other components.
This is Vue 3 with the Option API.
All of the components are imported by index.js in the component folder
import navbar from '#/components/navbar.vue'
import btn from '#/components/btn.vue'
export {
navbar,
btn
}
This is the navbar.vue:
<template>
<header class="navbar">
<ul>
<li>
<router-link to="/">Home</router-link>
</li>
<li>
<btn></btn>
</li>
</ul>
</header>
</template>
<script>
import { btn } from '#/components'
export default {
name: 'navbar',
components: {
btn
}
}
</script>
This is the btn.vue components:
<template>
<button class="btn" #click="$emit('click')">
<slot/>
</button>
</template>
<script>
export default {
name: 'btn'
}
</script>
Your index.js exported navbar and btn. And also you have already exported another component from navbar.vue in same file level. So make a separate folder for navbar and btn or change the export name.
I am new to frameworks. Hope this will work.
I would like to use JQuery inside my Angular components for DOM manipulation, but I would like to limit JQuery searches to the component's markup it's used in.
I'm trying using Shadow DOM, so I have this component:
import { Component, OnInit, ViewEncapsulation } from '#angular/core';
import * as $ from 'jquery';
#Component({
selector: 'app-layout',
templateUrl: './layout.component.html',
styleUrls: ['./layout.component.less'],
encapsulation: ViewEncapsulation.Native
})
export class LayoutComponent implements OnInit {
constructor() { }
ngOnInit() {
}
toggleSidebar(): void {
$('.main-sidebar').toggleClass('active');
$('.content-wrapper').toggleClass('expanded');
}
}
Here's the HTML:
<div class="wrapper">
<header class="main-header">
Logo
<nav class="navbar">
<button type="button" id="sidebar-collapse" class="btn btn-info navbar-btn" (click)='toggleSidebar()'>
<i class="fa fa-align-left"></i>
</button>
</nav>
</header>
<aside class="main-sidebar">
<section class="sidebar">
Sidebar
</section>
</aside>
<div class="content-wrapper">
<section class="content">
Content
</section>
</div>
</div>
If I remove the encapsulation configuration, it works, but if I leave it in place, JQuery can't find the elements searched. I would like to know how can I make JQuery find those elements. Is there any other way of limiting JQuery searches apart from using Shadow DOM?
Why would you do menu toggling with jQuery?
It is not good practice to use it with Angular application. Let Angular do it.
You can do it with host binding
HTML
<nav class="navbar">
<button type="button" id="sidebar-collapse" class="btn btn-info navbar-btn"
(click)='toggleSidebar()'>
<i class="fa fa-align-left"></i>
</button>
</nav>
<aside class="main-sidebar" [ngClass]="{'menu_open': menuOpen}">
<section class="sidebar">
Sidebar
</section>
</aside>
Component
import { Component, HostBinding, OnInit } from '#angular/core';
export class LayoutComponent implements OnInit {
// set it closed by default
#HostBinding('class.menu_open') public menuOpen = false;
....
toggleSidebar() {
this.menuOpen = !this.menuOpen;
}
...
}
I'm trying to implement a web application with aurelia and typescript.
I started from the aurelia-skeleton-typescript-webpack project as basis.
In the nav-bar i have also inserted a spinner for choose various city location, which should call a method which in turn publishes a message so that in the app.js a subscriber should display the view of the corresponding city.
I have implemented the view nav-bar-spinner.html and the view-model nav-bar-spinner.ts, which creates the spinner in the view nav-bar.html. The nav-bar.html is then inserted in the app.html as a template.
Each spinner item has a method that calls the publishLocation('city'), wich are bindet with the view-model nav-bar-spinner.ts.
Now when i click on a spinner item i receive the error: "Error: publishNavLocation is not a function"
I thing is a binding problem. I make a custom instantiation from object nav-bar-spinner in app.ts.
How i can do this binding correct?
I would be glad for some tips.
Here the code:
app.html
<template>
<require from="nav-bar.html"></require>
<nav-bar router.bind="router"></nav-bar>
<div class="page-host">
<div class="row">
<router-view swap-order="after" class="col-md-12"></router-view>
</div>
</div>
</template>
app.ts
import {Redirect} from 'aurelia-router';
import {Router, RouterConfiguration} from 'aurelia-router';
import {EventAggregator} from 'aurelia-event-aggregator';
import {inject} from 'aurelia-framework';
import {NavBarSpinner} from './nav-bar-spinner';
#inject(EventAggregator)
export class App
{
navBarSpinner;
constructor(private ea: EventAggregator)
{
this.navBarSpinner = new NavBarSpinner('hello world')
}
router : Router;
configureRouter(config: RouterConfiguration, router: Router)
{
config.title = 'bbv AmbientMonitor';
config.map([
{ route: '', name: 'login', moduleId: './login', nav: true, title: 'Anmeldung' },
{ route: 'live-view-all', name: 'live-view-all', moduleId: './live-view-all', nav: true, title: 'Live-Ansicht' },
{ route: 'live-view-zg', name: 'live-view-zg', moduleId: './live-view-zg', nav: true, title: 'Live-Ansicht' },
.
.
.
.
.
.
{ route: 'historical-view', name: 'historical-view', moduleId: './historical-view', nav: true, title: 'Historie-Ansicht'}
]);
this.router = router;
}
attached()
{
this.ea.subscribe('nav::toggleLogin', (data) =>
{
console.log('Subscribe data is: ' + data.nav);
this.router.navigateToRoute(data.nav);
});
}
}
nav-bar.html
<template bindable="router">
<require from="./nav-bar-spinner"></require>
<!-- <require from="nav-bar-spinner.html"></require> -->
<nav class="navbar navbar-default navbar-fixed-top" role="navigation">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#skeleton-navigation-navbar-collapse">
<span class="sr-only">Toggle Navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">
<i class="fa fa-home"></i>
<span>${router.title}</span>
</a>
</div>
<div class="collapse navbar-collapse" id="skeleton-navigation-navbar-collapse">
<ul class="nav navbar-nav">
<div class="pull-left">
<compose class="nav navbar-nav" view="nav-bar-spinner.html" view-model.bind="navBarSpinner"></compose>
</div>
<li repeat.for="row of router.navigation" class="${row.isActive ? 'active' : ''}">
<a data-toggle="collapse" data-target="#skeleton-navigation-navbar-collapse.in" href.bind="row.href">${row.title}</a>
</li>
</ul>
<!-- <ul class="nav navbar-nav navbar-right">
<li class="loader" if.bind="router.isNavigating">
<i class="fa fa-spinner fa-spin fa-2x"></i>
</li>
</ul> -->
</div>
</nav>
</template>
nav-bar-spinner.html
<template bindable="navBarSpinner">
<li class="dropdown">
<div as-element="compose" view-model.bind="navBarSpinner"></div>
Standort <span class="caret"></span>
<ul class="dropdown-menu">
<li>Zug</li>
<li>Zürich</li>
<li>Bern</li>
<li>Luzern</li>
<li>München</li>
</ul>
</li>
</template>
nav-bar-spinner.ts
import { EventAggregator } from 'aurelia-event-aggregator';
import { inject } from 'aurelia-framework';
import { View } from "aurelia-framework";
#inject(EventAggregator)
export class NavBarSpinner {
ea;
constructor(msg) {
this.ea = new EventAggregator();
}
publishNavLocation(navToCity) {
this.ea.publish('nav::toggleLogin', {nav: navToCity});
console.log("Method call publishLocationZug()");
}
}
Your problem lies with the following line:
view-model.bind="navBarSpinner"
Aurelia doesn't process this correctly. It is the name of the class, but you need to address it differently in an HTML attribute.
view-model.bind="nav-bar-spinner"
This tells Aurelia to look for a class named NavBarSpinner.
P.S. I also recommend you look into how Aurelia Dependency Injection works, you have quite some unnecessary (and false) code right now.
Thanks for the tips. After reading better the aurelia doc i resolved my problem.
Here my changes for typescript:
Chanded inject with autoinject
import { autoinject } from 'aurelia-framework';`
-
-
-
#autoinject( EventAggregator )
export class NavBarSpinner {
constructor(private ea: EventAggregator) {}
-
-
-
and in nav-bar.html i inserted the nav-bar-spinner.html with the binding view-model="nav-bar-spinner"
<div class="pull-left">
<compose class="nav navbar-nav" view-model="nav-bar-spinner"></compose>
</div>
and removed the other unnecessary bindings and requirements.
I've spent some time trying get the wiring for this working properly and can't. I don't know what I'm doing wrong. The best reference I've found for this issue so far is Aurelia Binding Click Trigger in Nav Bar. I tried that approach but am still getting the same error:
Uncaught Error: authenticate is not a function(…) in aurelia-binding.js:1965 (getFunction)
Here's what my setup looks like:
nav-bar.html
<template bindable="router, authenticate">
<nav class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#navbar-collapse-main">
<span class="sr-only">Toggle Navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">
<i class="fa fa-home"></i>
<span>${router.title}</span>
</a>
</div>
<div class="collapse navbar-collapse" id="navbar-collapse-main">
<ul class="nav navbar-nav">
<li repeat.for="row of router.navigation | authFilter: authenticated" class="${row.isActive ? 'active' : ''}">
<a data-toggle="collapse" data-target="#navbar-collapse-main.in" href.bind="row.href">${row.title}</a>
</li>
</ul>
<ul if.bind="authenticated" class="nav navbar-nav navbar-right">
<li>${userName}</li>
<li>Logout</li>
</ul>
<ul if.bind="!authenticated" class="nav navbar-nav navbar-right">
<li><a id="loginLink" click.trigger="authenticate()">Login</a></li>
<li> </li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li class="loader" if.bind="router.isNavigating">
<i class="fa fa-spinner fa-spin fa-2x"></i>
</li>
</ul>
</div>
</nav>
</template>
app.html
<template>
<require from="./views/shared/nav-bar.html"></require>
<require from="bootstrap/css/bootstrap.css"></require>
<nav-bar router.bind="router" authenticate.call="authenticate()"></nav-bar>
<div class="page-host">
<div class="container-fluid">
<router-view></router-view>
</div>
</div>
</template>
app.ts
import {inject, computedFrom} from "aurelia-framework";
import {Router, RouterConfiguration} from 'aurelia-router'
import {AuthService, AuthenticateStep} from 'aurelia-authentication';
import {log} from "./services/log";
#inject(AuthService)
export class App {
authService: AuthService;
router: Router;
userName: string;
constructor(auth) {
this.authService = auth;
}
configureRouter(config: RouterConfiguration, router: Router) {
config.title = 'AppName';
config.addPipelineStep('authorize', AuthenticateStep);
config.map([
{ route: ['', 'welcome'], name: 'welcome', moduleId: 'views/welcome', nav: true, title: 'Welcome' },
{ route: "orgTypes", name: "orgTypes", moduleId: "views/orgTypes", nav: true, auth: true, title: "Organization Types" },
{ route: "credits", name: "credits", moduleId: "views/credits", nav: true, auth: true, title: "Application Credits" }
]);
this.router = router;
}
authenticate() {
return this.authService.authenticate('identityServer')
.then((response) => {
log.info("login successful");
});
};
#computedFrom('authService.authenticated')
get authenticated() {
return this.authService.authenticated;
}
}
What is the proper setup to get a method in the App VM to bind in a subview?
Edit 1: Following FabioLuz second comment.
What Fabio has suggested is valid and should be working. You might have other issues preventing it from functioning.
Can you check it by simplifying App.authenticate() like this?
Just to rule out possible errors of underlying layer.
authenticate() {
log.info("login successful");
}
Another guess:
Is ./services/log a static object? Assuming it is not, injection might be missing for it.
Since you are using TypeScript, autoinject might help you to avoid similar pitfalls.
import {autoinject, computedFrom} from "aurelia-framework";
import {AuthService, AuthenticateStep} from 'aurelia-authentication';
import {log} from "./services/log";
#autoinject()
export class App {
authService: AuthService;
logger: log;
constructor(auth: AuthService, logger: log) {
this.authService = auth;
this.logger = logger;
}
}
What is the proper setup to get a method in the App VM to bind in a subview?
I know 3 possible solutions to achieve that (there may be more). I've created a gist showing these in action.
https://gist.run/?id=b9e8fee11e338e08bc5da7d4df68e2db
Use the dropdown to switch between navbar implementations. :)
1. HTML Only Element + bindables
This is your current scenario. See nav-bar-function.html in the demo.
2. <compose> + inheritance
Composition can be useful for some dynamic scenarios, but try not to overuse it. [Blog post]
When no model is provided, composed element inherits parent's viewmodel context.
Normally I would not recommend using it in your case. However, considering your issues with Solution 1., you could use this option for debug purposes. If you get the same error with <compose> as well, you may have a problem with App.authenticate() function itself.
Try it out in your solution by replacing
<nav-bar router.bind="router" authenticate.call="authenticate()"></nav-bar>
with
<compose view="./nav-bar.html"></compose>
This way, nav-bar.html behaves as a part of App component. See nav-bar-compose.html in the demo.
3. Custom Element + EventAggregator
You can use pub/sub communication between components* to avoid tight-coupling. Related SO answer: [Accessing Custom Component Data/Methods], and [Docs]
*components: custom elements with viewmodel classes
I hope it will help! :)
I am getting the error "TypeError: Cannot read property 'isFollowing' of null" for the following code:
import React from 'react';
import styles from './Cover.css';
import withStyles from '../../../../decorators/withStyles';
import Link from '../../../../utils/Link';
import Avatar from './Avatar';
import classnames from 'classnames';
import { Button } from 'react-bootstrap';
#withStyles(styles)
class Cover extends React.Component {
toggleFollow () {
this.setState({isFollowing: !this.state.isFollowing});
}
render() {
var user = this.props.user;
var followClass = this.state.isFollowing? 'active': '';
return (
<div className="Cover">
<div className="Cover-container">
<div>
<Avatar
username= {user}
profession="Web Developer"
location="New York, New York"
status="I am here to protect my business, a bunch of kids are out to ruin me" />
<div className="Cover-submenu-container">
<div className="Cover-submenu-section">
.
</div>
<div className="Cover-submenu-section links">
<a href="#" className="Cover-submenu-link">
<i className="fa fa-twitter"></i>
</a>
<a href="#" className="Cover-submenu-link">
<i className="fa fa-facebook"></i>
</a>
</div>
<div className="Cover-submenu-section connect-menu">
<Button className={classnames('follow-btn', {followClass})} href="#" onClick={this.toggleFollow.bind(this)}>Follow</Button>
<Button className="connect-btn" href="#" onClick={this.followBtn.bind(this)}>Connect</Button>
<Button className="follow-btn" href="#" onClick={this.followBtn.bind(this)}>Follow</Button>
</div>
</div>
</div>
</div>
</div>
);
}
}
export default Cover;
I could not figure out what I am doing wrong here, I am quite new to reactJS. Any idea anybody? Thanks a lot.
The first thing you need to do is to add the initial value of the isFollowing property. Because you are using ES6 syntax, it's possible to do that in the constructor. Just add this code before toggleFollow() function:
constructor(props) {
super(props);
this.state = {
isFollowing: false
}
}
The second error (based on the comments at your question) comes from not having the function followBtn() defined. Add this before render() function:
followBtn() {
alert('followBtn called'); //change it for whatever you want
}
Don't forget that clicking on both buttons (connect, follow) will now lead to the same result, because the same function will be called.