Child component not updating view when new data is passed through #Input() - javascript

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

How to render VueJS Slot in a another component

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

Angular 4 Accessing dom of another component inside routering component

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.

Multiple Router outlets in different templates Angular4

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.

Javascript vuejs component in for loop

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
},
}

How to open a route in a modal dialog in ember.js?

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

Categories

Resources