I have an Angular 5 app where I have a list of projects. A user clicks on a project and the project details are displayed in a popup. The detail view is the child component. I pass the data to the detail view via #Input
Parent Component Template:
// User clicks this button to pass the "clicked upon" project to the onSelect() function.
<a class="uk-button uk-button-default" id="edit-btn" (click)="onSelect(project)" uk-toggle="target: #project-detail">View Project Details</a>
// Nested Child Component
<project-detail [project]="selectedProject"></project-detail>
Parent Component:
// The onSelect() function
onSelect(project: IProject) {
this.selectedProject = JSON.parse(JSON.stringify(project));
}
Child Component:
#Input() project: IProject;
Child Component Template:
<div id="fullName"><h1>{{project?.firstName + " " + project?.lastName}}</h1></div>
<div id="title"><h2>{{project?.title}}</h2></div>
<div id="summary"><p>{{project?.summary}}</p></div>
The Issue:
The issue only arises sometimes but if the user select Project A to view its detail and then selects Projects B to view its detail. The details view shows Projects A's data. No matter what project the user clicks on the the details view will only show Project A's detail.
What am I doing wrong?
Related
VueJS slot could be placed anywhere inside a parent component.
However, I have a use case where I need to show a slot in another (foreign) component outside the parent component.
For example, I have component Student:
<template>
<slot name="detail" />
<slot name="status" /> <!-- I don't want to render this slot inside this component, but somewhere else -->
</template>
I want to display the student's status in a header section outside the student component as below:
<div class="Header">
<!-- student status slot should go here-->
<student-status-slot></student-status-slot>
</div>
<div class="InputForm">
<student>
<template #detail>This the detail information about student</template>
<template #status>Grade A</template>
</student>
</div>
You may suggest to create a new "student-status-slot" component. However, the idea is to have all student related information, including "status" declared under the component, but render some of the slot somewhere else.
I have tried another approach creating a separate component and pass the status data via prop. But That is not what I wanted to achieve. I don't want to pass data, but re-use the student status component, which can be dynamic.
In a parent component you can access child's slots by adding ref attribute to child, and then render the VNode obtained. See the demo here https://codesandbox.io/s/happy-einstein-9f5rke?file=/src/components/StudentPage.vue
But actually I wouldn't recommend this way such it's not conventional and slots were not designed for this. I think you should rather consider using portals (Vue 3 portals or https://www.npmjs.com/package/portal-vue if you use Vue 2) to achieve your goal
I have created toolbar and related components in my app
So here is layout I have created
<div fxLayout="column">
<div>
<app-platformbar ></app-platformbar>
</div>
<div>
<router-outlet></router-outlet>
</div>
</div>
platforbar component is common for all routing
<md-toolbar color="primary" >
<span><h2>{{Name}}</h2></span>
<span *ngIf="showLogoutBtn"><button md-raised-button color="warn" (click)="logout()">Logout</button></span>
</md-toolbar>
Inside platforBar component I have created one logout button which I don't want show on login( component) router
I have stored user object inside userservice to identify whether the user is logged in or not.
based on logged in I want to hide logout button.
So what I want to once user click on logout button from home router It will redirect it to login router but logout button from platformbar component is still there
So I want to hide it on login router.
I login using router-outlet tag in main app.component.html and if the login is successful i navigate to home page which is a different component consists of toolbar,side-nav and an area to display my dashboard content.Now in my home page i need another router-outlet where it displays the content upon choosing option in side-nav
my project structure is :
src
--login
----login.component.html
----login.component.ts
----login.component.css
--home-page
dashboard
--dashboard.component.html
--dashboard.component.ts
----home.component.html
----home.component.ts
----home.component.css
--application-details
app-overview
--app-overview.component.html
--app-overview.component.ts
----application.component.html
----application.component.ts
--users
----users.component.html
----users.component.ts
In my app.component.html has the following
<router-outlet></router-outlet>
In login.componnet.ts
upon success this.route.navigate(['/home-page'])
In my app-routing.module.ts
{ path:'', redirectTo:'/login',pathMatch:'full'},
{ path: 'login', component: LoginComponent},
{ path:'/home-page',component:HomePgaeComponent,
children:[
{ path:'',component:DashboardComponent,outlet:'dashboard'},
{ path:'user',component:UsersComponent,outlet:'user'}, ]
},
now as soon as i login based on success condition triggers the page
navigates to dashboard which is inside home page like this
<tool-bar></tool-bar>
<div>
<side-menu></side-menu>
<div class="main-content">
<router-outlet name="dashboard"></router-outlet>
<router-outlet name="user"></router-outlet>
</div>
</div>
here when i click upon the user option from side-nav it appends dashboard
content as well as "user" content.I have other options as well so for the
very first time it loads the landing page perfectly but when i select other
side-nav options all the other content is mixed up. So When i select options
from side-nav that respective content should be loaded in content area of
home page.another scenario is when i navigate to homepage i need to navigate
to another page when i perform some click action in dashboard component.
I have gone through tutorials and there they were passing some ids through
services and i have no idea how to do that. As per my knowledge i know that
passing outlet name into router-outlet will fetch the content of that
component likewise as many router i mention those many multiple routers will
be generated.
here when i click upon the user option from side-nav it appends
dashboard content as well as "user" content
<router-outlet name="dashboard"></router-outlet>
<router-outlet name="user"></router-outlet>
Both are children of same component
{ path:'/home-page',component:HomePgaeComponent,
children:[
{ path:'',component:DashboardComponent,outlet:'dashboard'},
{ path:'user',component:UsersComponent,outlet:'user'}, ]
},
Either you use same router outlet to render both dashboard and user or you have user router outlet inside dashboard and user is child of dashboard.
I have a v-for loop in vuejs that displays a component on each iteration. This is an autocomplete component that searches and displays product names when a user types in the input box.
I have a #change="setProduct" attribute on each component that correctly calls my setProduct method in my parent component.
But how can I know which of component was updated? All thats passed to the setProduct method is the details of the product that was emitted, but I don't know which component emitted the event to know which line to update.
Here is some relevant code:
This is in the parent component
<template>
<div class="row" v-for="line, i in invoice.InvoiceLines">
<div class="col-xs-5">
<auto-complete :list="productList" :value="line.Product.name" #change="setProduct"></auto-complete>
</div>
...
</div>
</template>
<script>
export default {
data() {
return {
invoice:{},
productList:[]
},
}
methods:{
setProduct(product){
//product has the details of the new product that was selected. But I don't know which invoice line it is referring to.
},
}
}
</script>
The component responds to a user clicking a selection in a dropdown, and then issues $emit('change', product);
The component has no knowledge of the parent component, so it doesn't know which invoice line it refers to. I could pass the index into the child component and then pass it back out, but that seems anti-pattern for vue.
Maybe there is an easier way for me to go about this?
Thanks for your help.
Since you're using v-for, so you can actually retrieve the index of the items in invoice.InvoiceLines and you can pass whatever your want into setProduct:
<template>
<div class="row" v-for="(line, i) in invoice.InvoiceLines">
<div class="col-xs-5">
<auto-complete
:list="productList"
:value="line.Product.name"
#change="setProduct(line, i, $event)"></auto-complete>
</div>
...
</div>
</template>
Then in JavaScript:
methods: {
setProduct(product, index, event){
// product - the 'line' responsible in invoice.InvoiceLines
// index - the index of line in invoice.InvoiceLines
// event - the event object
},
}
We have a requirement of opening a modal dialog containing a route or a component. We are looking for some modal components, and saw ember-bootstrap's modal is useful.
So,
How can we open any route as a modal dialog ? (If parent route decides a route to be open in a modal, the child route should be open in modal.)
Can we create a service, to pop up a modal dialog? Such as: ModalDialogService.popup(title, bodyComponent, commitHandler, cancelHandler); or ModalDialogService.popup(title, routeName, commitHandler, cancelHandler); And how can we do this without violating the Data Down Action Up principle?
Is there any guide, document, tutorial or npm package for implementing modals in ember.js?
UPDATED:
What I need is to open any of the current routes in a modal. For example, in a given route hierarchy:
-module1
|-module1.query
|-module1.add
|-module1.update
|-module1.delete
Currently module1.query has transitions to others. But I want to give an option to the module developers to open any of the add, update, delete routes in a modal. So that query route doesn't lose its state, when an add operation finished.
Also we have some services used by components. At some conditions, services need to display a modal that has a component.
You should be able to use a service and component similar to one below to achieve what you want.
Have a look at the twiddle for a demo of how this works exactly, and the code below for quick reference
Your route template could look something like this.
// templates/hasmodal.hbs
{{#bs-modal}}
Modal Content
{{/bs-modal}}
Your route hooks, with service injected
// routes/hasmodal.js
export default Ember.Route.extend({
modalNavigation: Ember.inject.service(),
activate(){
console.log('openingModal')
this.get('modalNavigation').openModal()
},
deactivate(){
console.log('closingModal')
this.get('modalNavigation').openModal()
},
actions: {
onClose(){
console.log('we want to close route')
}
}
})
Your bs-modal or relevant component
//components/bs-modal.js
export default Ember.Component.extend({
modalNavigation: Ember.inject.service(),
isOpen: Ember.computed.alias('modalNavigation.modalOpen'),
classNameBindings: ['isOpen:modalDialog:notOpen'],
actions: {
close(){
this.get('modalNavigation').closeModal()
}
}
})
The bs-modal component template
// templates/components/bs-modal
<div>
{{yield}}
</div>
<button class='close' {{action 'close'}}>Close Me</button>
Your Modal Service to manage state
// services/modal-navigation.js
export default Ember.Service.extend({
modalOpen: false,
openModal(){
this.set('modalOpen',true)
},
closeModal(){
this.set('modalOpen',false)
}
})
UPDATE:
updated twiddle
It basically nests routes that contain a modal underneath a route you want to preserve the state of and show behind the modal.
// router.js [truncated]
Router.map(function() {
this.route('module1',function(){
this.route('query',function(){
this.route('add')
this.route('update', { path: '/update/:item_id' })
this.route('delete', { path: '/delete/:item_id' })
})
})
// templates/modules1/query.hbs
Queried List {{link-to 'add item' 'module1.query.add'}}<br/>
<ul>
{{#each model as |item|}}
<li>
{{item.id}}-{{item.title}}
{{link-to 'u' 'module1.query.update' item}}
{{link-to 'd' 'module1.query.delete' item}}
</li>
{{/each}}
</ul>
{{outlet}}
// templates/module1/query/add.hbs
{{#modal-component isOpen=true onClose=(action "routeClosed")}}
<div>
Title:{{input value=model.title}}
</div>
<button {{action 'save'}}>Save</button>
{{/modal-component}}
Where all the other sub components follow the same modal wrapper principle