angular 1.5 component router pass values or callbacks between components - javascript

I created app with angularjs 1.5 component router, how could I let parent component pass values to child component inside and child component binds callbacks to the parent component like:
app.js
angular.module('newsApp',['ngComponentRouter'])
.config(['$locationProvider', function($locationProvider) {
$locationProvider.html5Mode(true);
}])
.value('$routerRootComponent', 'myNews')
components.js
angular.module('newsApp')
.component('myNews', {
templateUrl: '/myNews.html',
controller: ['newsFactory',ShamraNewsCtrl],
$routeConfig: [
{path: '/', name: 'MyLatestNews', component: 'myLatestNews', useAsDefault: true},
{path: '/:category', name: 'MyNewsList', component: 'myNewsList'},
]
}).component('myLatestNews', {
templateUrl: '/latestNews.html',
controller: ['newsFactory', myLatestNewsCtrl],
}).component('myNewsList', {
templateUrl: '/newsList.html',
controller: ['newsFactory', myNewsListCtrl],
})
myNews.html
<div class="input-group">
<input type="text" class="form-control" ng-bind="$ctrl.query" placeholder="Search for...">
<span class="input-group-btn">
<button class="btn search-btn btn-success" type="submit">
<i class="fa fa-search"></i>
</button>
</span>
</div>
<div>
<div class="sh-news-cat list-group">
<a ng-repeat="(key, catVal) in $ctrl.categories" class="list-group-item" ng-class="{active:$ctrl.select.selected == key}" ng-link="['MyNewsList', {category: key}]">
{{ catVal}}
<span ng-show="$ctrl.selected.spinner == key"><i class="fa-lg fa fa-spinner fa-spin"></i></span>
</a>
</div>
</div>
<div>
<ng-outlet></ng-outlet>
</div>
latestNews.html
<div class="last-news-container">
<div class="latest-news" ng-repeat="(key, newsList) in $ctrl.latestNews">
<h3 class="section-title">
{{ newsList.title}}
</h3>
<hr>
<div class="section-body">
<div class="news-item" ng-repeat="sectionList in newsList.result">
<div class="news-info">
<h3>{{ sectionList.title }}</h3>
<p>{{ sectionList.content }}</p>
</div>
</div>
</div>
<div class="more-news">
<a ng-link="['MyNewsList', {category: key}]" ng-hide="$ctrl.seemore"> see more ... </a>
</div>
</div>
</div>
so when I click on seeMore in latestNews.html I want to (sent to OR change) category value in myNews Component
and when use type in input box i need to pass this value to another component

Related

Angular Router duplicates my route component

I have Screen shots of the application: https://ibb.co/NmnSPNr and https://ibb.co/C0nwG4D
info.component.ts / Info component where child component of Item component, this is displayed when I route to this some link.
export class InfoComponent implements OnInit {
p: Post;
paramsSubscription: Subscription;
constructor(private route: ActivatedRoute) {}
ngOnInit(): void {
this.p = {
id: this.route.snapshot.params['id'],
name: this.route.snapshot.params['name'],
price: this.route.snapshot.params['price'],
place: this.route.snapshot.params['place'],
image: this.route.snapshot.params['image'],
date: this.route.snapshot.params['date'],
desc: this.route.snapshot.params['desc'],
};
**item.component.ts ** / Item component only fetches HTTP request and displayes items
export class ItemComponent implements OnInit {
items: any;
searchValue: any;
constructor(private http: HttpService) {}
ngOnInit(): void {
this.getAllPosts();
}
//GetPost's
getAllPosts() {
this.http.getPost().subscribe((res) => {
this.items = res;
});
}
}
info.component.html / This is the info component where I store the data from info.ts
<div class="container">
<div class="col-xs-12">
<div class="imagee-div">
<img [src]="p.image" alt="snkr1" />
</div>
<div class="details">
<h3>{{ p.id }}. {{ p.name }}</h3>
<small>release date: {{ p.date }}.</small>
<p>Price: ${{ p.price }}</p>
<h4>More Details:</h4>
<p>
{{ p.desc }}
</p>
</div>
</div>
</div>
item.component.html / This is my Parent component, here I store the router outlet, routerLink and loop over the items
<div class="row mx-auto">
<div class="col-4">
<div class="col-12 mt-3" id="main-card">
<div class="row" id="card" *ngFor="let p of items | filter: searchValue">
<div class="img-div">
<img [src]="p.image" alt="" width="250px" />
</div>
<div class="main">
<h4>{{ p.id }}. {{ p.name }}</h4>
<p>Price: ${{ p.price }}</p>
<small>{{ p.place }}</small
><br />
<a
href="#"
[routerLink]="[
'/item',
p.id,
p.name,
p.date,
p.desc,
p.price,
p.image
]"
>
<button class="btn btn-success mt-1">
<i class="fas fa-question"></i>
</button>
</a>
</div>
</div>
</div>
</div>
<div class="col-8"><router-outlet></router-outlet></div>
</div>
Instead of using the <router-outlet>, you can use <app-info-component> and add *ngIf condition in the item component.
Instead of this:
<div class="col-8"><router-outlet></router-outlet></div>
Use this:
<div class="col-8">
<app-info-component *ngIf="p.id > 0">
</app-info-component>
</div>
Same in the info-component add ngIf condition has below.
<div *ngIf="p.id > 0" class="container">
<div class="col-xs-12">
<div class="imagee-div">
<img [src]="p.image" alt="snkr1" />
</div>
<div class="details">
<h3>{{ p.id }}. {{ p.name }}</h3>
<small>release date: {{ p.date }}.</small>
<p>Price: ${{ p.price }}</p>
<h4>More Details:</h4>
<p>
{{ p.desc }}
</p>
</div>
</div>
</div>

Vue js broadcast not working

I have this Vue instance:
var vm = new Vue({
el: '#app',
data: {
user: {
email: '',
password: '',
passwordconfirm: '',
bars: {},
authentication_token: '',
id: 0
}
},
components: {
dashboard: dashboard
},
events: {
'onLoginSuccesfull': function(user) {
// do stuff with user obj
this.$broadcast('onUserLoggedIn');
}
}
}
It has a 'dashboard' component:
var dashboard = Vue.extend({
template: '#dashboard',
data: function() {
return {
currentPage: 'main'
}
},
components: {
'dashboard-mainpage': dashboardmainpage
}
events: {
'onUserLoggedIn': function() {
alert('I should be firing bu I am not!');
}
}
});
Vue.component('dashboard', dashboard);
The 'dashboard' has a 'dashboard-mainpage' component:
var dashboardmainpage = Vue.extend({
template: '#dashboard-mainpage',
methods: {
loadData: function() {
this.$http.get('/dashboard/main.json', {
authenticity_token: window._token
}).then(function(response) {
var data = JSON.parse(response.body);
console.log(data);
}, function(response) {
console.log(response);
this.$dispatch("onRequestUnauthorized");
});
}
},
events: {
'onUserLoggedIn': function() {
alert('I should be firing as well but I am not!');
this.loadData();
}
}
});
The problem is that when the 'onLoginSuccesfull' event is triggered on the main Vue instance, the subsequently broadcasted 'onUserLoggedIn' on the 1st and 2nd level descendant are not triggered, when calling 'this.$broadcast('onUserLoggedIn');'.
I know that event callbacks must return true to keep propagating down the chain but the first event callback (on 'dashboard') is not even triggered.
What am I missing?
Thanks!
===== EDIT =====
Main app:
<div id="app">
<dashboard v-if="currentStep == 'dashboard'" v-bind:appname="appName" v-bind:dashboard-vm="dashboard" v-bind:user="user" v-bind:bar.sync="bar"></dashboard>
</div>
Dashboard template:
<template id="dashboard">
<div class="container body">
<div class="main_container">
<div class="col-md-3 left_col">
<div class="left_col scroll-view">
<div class="navbar nav_title" style="border: 0;">
<span>The Bar</span>
</div>
<div class="clearfix"></div>
<br />
<!-- sidebar menu -->
<div id="sidebar-menu" class="main_menu_side hidden-print main_menu">
{{ user.email }}
<div class="menu_section">
<ul class="nav side-menu">
<li><a v-on:click="createNewBar"><i class="fa fa-plus"></i> Create new bar</a>
</li>
<li><a v-on:click="showDashboard"><i class="fa fa-bar-chart"></i> Dashboard</a>
</li>
<li><a v-on:click="showBars"><i class="fa fa-bars"></i> Bars</a>
</li>
<li><a><i class="fa fa-tachometer"></i> Settings <span class="fa fa-chevron-down"></span></a>
<ul class="nav child_menu">
<li><a v-on:click="showProfile">Profile</a></li>
<li><a v-on:click="showPaymentsPlans">Plan & payments</a></li>
</ul>
</li>
</ul>
</div>
</div>
<!-- /sidebar menu -->
</div>
</div>
<!-- top navigation -->
<div class="top_nav">
<div class="nav_menu">
<nav>
<div class="nav toggle">
<a id="menu_toggle"><i class="fa fa-bars"></i></a>
</div>
<ul class="nav navbar-nav navbar-right">
<li class="">
<a v-on:click="$parent.logoutUser" style="cursor: pointer">Log out</a>
</li>
</ul>
</nav>
</div>
</div>
<!-- /top navigation -->
<!-- page content -->
<div class="right_col" role="main">
<dashboard-mainpage v-if='currentPage == "main"' v-bind:vm="dashboardVm"></dashboard-mainpage>
</div>
</div>
<!-- /page content -->
<!-- footer content -->
<footer>
<div class="pull-right">
© <%= Time.now.year %> The Bar
</div>
<div class="clearfix"></div>
</footer>
<!-- /footer content -->
</div>
</div>
</template>
Dashboard-mainpage:
<template id="dashboard-mainpage">
<div class="col-md-12 x_title">
<div>
<div class="btn-group pull-right">
<button type="button" class="btn btn-info"><span class="fa fa-calendar" aria-hidden="true"></span> Today</button>
<button type="button" class="btn btn-info dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="caret"></span>
<span class="sr-only">Toggle Dropdown</span>
</button>
<ul class="dropdown-menu">
<li>
Today
</li>
<li>
Yesterday
</li>
<li>
Last 7 days
</li>
<li>
Last 30 days
</li>
<li>
Last year
</li>
<li>
All
</li>
</ul>
</div>
<span class="pull-right">Filter:</span>
</div>
</div>
<!-- top tiles -->
<div class="row top_tiles">
<div class="animated flipInY col-lg-4 col-md-4 col-sm-6 col-xs-12">
<div class="tile-stats">
<div class="icon"><i class="fa fa-bars"></i></div>
<div class="count">{{ vm.totalBars }}</div>
<h3>Total bars</h3>
<p>All you active bars</p>
</div>
</div>
<div class="animated flipInY col-lg-4 col-md-4 col-sm-6 col-xs-12">
<div class="tile-stats">
<div class="icon"><i class="fa fa-eye"></i></div>
<div class="count">{{ vm.totalViews }}</div>
<h3>Total views</h3>
<p>Total number of bar views</p>
</div>
</div>
<div class="animated flipInY col-lg-4 col-md-4 col-sm-6 col-xs-12">
<div class="tile-stats">
<div class="icon"><i class="fa fa-mouse-pointer"></i></div>
<div class="count">{{ vm.totalClicks }}</div>
<h3>Total clicks</h3>
<p>Total number of clicks</p>
</div>
</div>
</div>
<!-- /top tiles -->
<div class="row">
<div class="col-md-12 col-sm-12 col-xs-12">
<div class="dashboard_graph">
<div class="row x_title">
<div class="col-md-6">
<h3>Statistics</h3>
</div>
<div class="col-md-6">
<div id="reportrange" class="pull-right" style="background: #fff; cursor: pointer; padding: 5px 10px; border: 1px solid #ccc">
<i class="glyphicon glyphicon-calendar fa fa-calendar"></i>
<span>December 30, 2014 - January 28, 2015</span> <b class="caret"></b>
</div>
</div>
</div>
<div class="col-md-12 col-sm-12 col-xs-12">
<div class="x_title">
<h2>Sub title</h2>
<div class="clearfix"></div>
</div>
<p>stats here</p>
</div>
<div class="clearfix"></div>
</div>
</div>
</div>
<br />
</template>
Ok. For anyone who is going to wander onto this "issue". Here's the fix.
I used 'v-if' attributes on the children that were listening to the 'onUserLoggedIn' event. The docs for 'v-if' say:
Conditionally render the element based on the truthy-ness of the
expression value. The element and its contained data bindings /
components are destroyed and re-constructed during toggles. If the
element is a element, its content will be extracted as the
conditional block.
So, when 'v-if = "something false"' on some child component it practically does not exist and thus is not able to do or respond to anything, including events.
When using 'v-show' instead of 'v-if' to conditionally toggle certain components, the component stays "alive" and will be able to respond.
Some stuff going on:
If you define a component in another component (as you are doing in the app component, there is no need to define this component globally (https://vuejs.org/guide/components.html#Registration-Sugar).
If a component is not "called" inside another component's template, you never establish a relation parent-children between them. I can't see the template of the App component, so I assume there is none.
There is some bits of code missing, but I assume that onLoginSuccesfull never gets actually called on the App component to begin with!
As you say, if you want an event to propagate after it's been triggered for the first time, this triggering function must return true.
Here's a working jsfiddle with the basics covered: https://jsfiddle.net/e85ekxk1/2/
Good luck!

Angular 2 - Reload component when routerLink clicked again

I am working on Angular 2 project with following file structure.
HeaderComponent.ts
AppComponent.ts
Page1Component.ts
Page2Component.ts
I have following template in my HeaderComponent.ts
<nav class="navbar navbar-default navbar-fixed-top" role="navigation">
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#myNavbar">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">WebSiteName</a>
</div>
<div class="collapse navbar-collapse" id="myNavbar">
<ul class="nav navbar-nav">
<li class="active"><a [routerLink]="['']">Home</a></li>
<li><a [routerLink]="['/page1']" >Page1</a></li>
<li><a [routerLink]="['/page2']">Page2</a></li>
</ul>
</div>
</div>
</nav>
with following routes in my AppComponent
#Component({
selector: 'my-app',
template: `
<my-header></my-header>
<router-outlet></router-outlet>
`,
directives: [ROUTER_DIRECTIVES, HeaderComponent]
})
#Routes([
{path: '/', component: HomeComponent},
{path: '/page1', component: Page1Component}
{path: '/page2', component: Page2Component}
])
export class AppComponent {
ngAfterViewInit() {
//To show the active tab in navbar
$(".nav a").on("click", function () {
$(".nav").find(".active").removeClass("active");
$(this).parent().addClass("active");
});
}
}
and my Page1Component has following sample form
<section class="col-md-8 col-md-offset-2">
<form [ngFormModel]="myForm" (ngSubmit)="onSubmit()">
<div class="form-group">
<label for="firstName">First Name</label>
<input [ngFormControl]="myForm.find('firstName')" type="text" id="firstName" class="form-control">
</div>
<div class="form-group">
<label for="lastName">Last Name</label>
<input [ngFormControl]="myForm.find('lastName')" type="text" id="lastName" class="form-control">
</div>
<div class="form-group">
<label for="email">Mail</label>
<input [ngFormControl]="myForm.find('email')" type="email" id="email" class="form-control">
</div>
<div class="form-group">
<label for="password">Password</label>
<input [ngFormControl]="myForm.find('password')" type="password" id="password" class="form-control">
</div>
<button type="submit" class="btn btn-primary" [disabled]="!myForm.valid">Sign Up</button>
</form>
</section>
So when I click on Page1 routerLink in header <li><a [routerLink]="['/page1']">Page1</a></li>, it loads the Page1Component in <router-outlet></router-outlet>. I fill some details in form and when I click on Page1 routerLink again in header before submitting the form, I want Page1Component to reload so my form comes to initial state but it doesn't do anything on click. I tried to reset form in routerOnActivate() and routerCanDeactivate() but none of the functions being called. So basically, I want my component to load again when I click on [routerLink]
Please let me know if I can explain better.
You can take care of this scenario by using dummy route,
<a (click)="changeRoute(url)">
Add one dummy route to your router:
{ path: 'dummy', component: dummyComponent }
dummyComponent.ts
//Dummy component for route changes
#Component({
selector: 'dummy',
template: ''
})
export class dummyComponent{}
Now inside your component add changeRoute function:
changeRoute(url) {
this.router.navigateByUrl('/dummy', { skipLocationChange: true });
setTimeout(() => this.router.navigate(url));
}
// 'skipLocationChange' will avoid /dummy to go into history API
This will re render your component and rest all will be taken care by Angular.
You could use the (click) event an navigate in code to some dummy component and then again back to the previous component. There is a parameter in router.navigate() to skip history changes so the back button keeps working with this hack.
Another approach would be to remove the routerLink, and naviagate via code:
<a (click)="gotoPage1()">
Now gotoPage1 just appends a #X number to existing url
...
count: number = 2;
gotoPage1(){
count++;
return this.router.navigate(...'#'+count);
}

Angular routes for a detailed view, using Foundation

Problem
New to Angular and I'm using "Foundation for Apps: to help with building a business directory in Angular. I'm looking to create routes for a detailed view for each of the businesses, which would look like http://localhost:8080/nameofbusiness.
I've been reading the Angular docs and this example pretty much does exactly what I'm trying to do, I've tried using in a similar snippet (see below) into app.js, but it doesn't seem to be compiling correctly.
Github: https://github.com/onlyandrewn/angular
Snippet
phonecatApp.config(['$routeProvider',
function($routeProvider) {
$routeProvider.
when('/phones', {
templateUrl: 'partials/phone-list.html',
controller: 'PhoneListCtrl'
}).
when('/phones/:phoneId', {
templateUrl: 'partials/phone-detail.html',
controller: 'PhoneDetailCtrl'
}).
otherwise({
redirectTo: '/phones'
});
}]);
app.js
'use strict';
var myApp = angular.module('application', [
'ui.router',
'ngAnimate',
//foundation
'foundation',
'foundation.dynamicRouting',
'foundation.dynamicRouting.animations'
])
.config(config)
.run(run)
;
config.$inject = ['$urlRouterProvider', '$locationProvider'];
function config($urlProvider, $locationProvider) {
$urlProvider.otherwise('/');
$locationProvider.html5Mode({
enabled:false,
requireBase: false
});
$locationProvider.hashPrefix('!');
}
function run() {
FastClick.attach(document.body);
}
home.html (Main view of all the businesses)
---
name: home
url: /
---
<div ng-controller="MainCtrl">
<header>
<p class="sponsored" id="top">Sponsored by </p>
<img src="http://placehold.it/200x30" class="sponsors" alt="">
<h1>Business Directory</h1>
<div class="find">
<input type="search" placeholder="What are you looking for?" ng-model="query">
</div><!-- /.find -->
</header>
<div class="businesses">
<div class="storeIcon">
<img src="/assets/img/store.png" class="store" alt="">
</div><!-- /.storeIcon -->
<p class="number">Search {{businesses.length}} businesses in Brandon</p><button class="filter button">Filter by <i class="fa fa-chevron-down"></i></button>
<div class="options">
<div class="cat">
<div class="categories">
<div class="group">
<p class="name">Grade Level</p>
<div class="check">
<input type="radio" name=""><p>Auto</p>
<input type="checkbox" name=""><p>Restaurant</p>
<input type="checkbox" name=""><p>Other</p>
</div><!-- /.check -->
</div><!-- /.group -->
<div class="group">
<p class="name">School Type</p>
<div class="check">
<input type="checkbox">
<input type="checkbox">
<input type="checkbox">
<input type="checkbox">
<input type="checkbox">
</div><!-- /.check -->
</div><!-- /.group -->
</div><!-- /.categories -->
</div><!-- /.cat -->
</div><!-- /.options -->
</div><!-- /.businesses -->
<div class="all">
<div class="business large-4.columns" data-ng-repeat="business in businesses | filter:query | orderBy:'name'" >
<div class="overlay">
<img src="http://placehold.it/300x300" class="storefront" alt="">
</div><!-- /.overlay -->
<div class="info">
<p class="name">{{business.name}}</p>
<p class="description">{{business.description}}</p>
<p class="address">{{business.address}}</p>
{{business.website}}
</div><!-- /.info -->
</div>
</div>
<footer>
<hr>
<i class="fa fa-twitter"></i>
<i class="fa fa-facebook"></i>
<div class="backContainer">
<p class="back">Back to top</p>
</div><!-- /.backContainer -->
</footer>
</div>
expanded.html (Detailed view of just one of the businesses)
---
name: expand
url: /:id
---
<div ng-controller="MainCtrl">
<p>This is the expanded view for each of the businesses</p>
</div>
You will need to define a parent view for your expanded template using the simplified built in plugin, then foundation for apps will use that to generate the proper $routeProvider's configuration. You can also use that same plugin to define a wrapper controller for it.
Your expanded.html file may look like this :
---
name: expand
url: /:id
parent: home
controller: EditCtrl
---
<p>This is the expanded view for each of the businesses</p>
<pre> {{$stateParams}}</pre>
now if /#!/home is the url to what you decided to be a parent page or product listing page, then /#!/home/nameofbusiness will take you to it's child page "expanded", and it will set the id value of $stateParams to "nameofbusiness" $stateParams -> {"id":"nameofbusiness"} which will be accessible directly in your expand template or within it's related controller if you need to build some logic on top of it.
You will find more details in Foundation for apps Dynamic Routing documentation

Hiding certain HTML elements when using ng-view (Angularjs)

I'm working on one of my first projects in AngularJS, and I'm using ng-view with $routeProvider for my templating, but I've run into a small problem. On a few of my pages (landing, login, and registration) I don't want the navbar to show; but I want the navbar to show on all my other views. What are my options in fixing this problem using AngularJS?
The link below includes the index.html, login.html, and my $routeProvider
index.html
<body ng-app="hello">
<!-- FIXED NAVBAR -->
<div class="navbar navbar-default navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".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="#">hello</a>
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav navbar-right">
<li>Add Group</li>
<li>Upload File</li>
<li class="active">Notifications</li>
</ul>
</div>
</div>
</div>
<div class="container">
<div ng-view></div>
</div>
</body>
login.html
<div class="container">
<form ng-controller="AdminUserCtrl" class="form-signin" role="form" method="post">
<h2 class="form-signin-heading">Please sign in</h2>
<input type="email" class="form-control" id="inputUsername" placeholder="Email address" ng-model="login.email" required autofocus>
<input type="password" class="form-control" id="inputPassword" placeholder="Password" ng-model="login.password" required>
<div class="checkbox">
<label>
<input type="checkbox" value="remember-me"> Remember Me
</label>
</div>
<button class="btn btn-lg btn-primary btn-block" type="submit" ng-click="logIn(login.email, login.password)">Sign In</button>
<p style="text-align: center; margin-top: 5px;"><a style="text-align: center;" href="#/register">Register A New Account</a></p>
</form>
$routeprovider
hello.config([ '$locationProvider', '$routeProvider',
function($location, $routeProvider) {
$routeProvider.
when('/', {
templateUrl: 'views/login.html',
controller: 'AdminUserCtrl',
access: { requiredLogin: false}
}).
when('/feed', {
templateUrl: 'views/feed.html',
controller: 'FeedListCtrl',
access: { requiredLogin: true}
otherwise({
redirectTo: '/'
});
}]);
The hack I'm currently thinking of to fix this is taking out the html code for the navbar out of the index and only adding it in the pages I need it, but i was wondering if there is another fix?
The navigation bar and other components that are separate from ng-view should have their own controllers. You can communicate information to these controllers from controllers within the view using a service. Properties of this service can be bound to the navigation views and their display can be updated based on the values of the properties. For example.
app.factory("navBarService", function () {
return {show: true};
});
app.controller("NavBarController", function (navBarService) {
this.show = navBarService.show;
});
app.controller("SomeControllerYouRouteToController", function (navBarService) {
navBarService.show = false;
});

Categories

Resources