What I Want
pass an object to the Vue component being rendered to display the object's info
What I'm Doing
Page Controller
<?php
namespace App\HTTP\Controllers;
use Inertia\Inertia;
use App\Http\Controllers\Controller;
use App\Models\project;
class ProjectController extends Controller {
public function project(project $project){
return Inertia::render('Projects/ProjectPage', [
'project' => [
'id' => $project->id,
'projectImage' => $project->projectImage,
'projectText' => $project->projectText,
]
]);
}
}
Route To Page
<div v-for="project in this.$page.props.projects" :key="project.id" class="flex flex-row py-2 hover:shadow-lg">
<inertia-link :href="route('projectPage', project.id)" > <ProjectTile :project="project" class="py-4"/> </inertia-link>
</div>
Page Rendering Object
<template>
<!-- Root div -->
<div>
This is the project page
{{project.projectName}}
</div>
<!-- End Root div -->
</template>
<script>
export default {
props: {
project: Object,
}
}
</script>
Route In routes/web.php
Route::get('projects/{projectid}', [ProjectController::class, 'project'])->name('projectPage');
What This Results In
page:Object
props:Object
errors:Object (empty)
project:Object
id:null
projectImage:null
projectText:null
param passed to controller here
public function project(project $project){ ... }
must be the same as what is referenced in route here
Route::get('projects/{projectid}')...
solution:
public function project(project $projectid){ ... }
Related
i have a custom component where i am passing some data to render some css styles
like
<title :profile="true" :class="true"/>
in my component
i have a div as:
<div class="tabletitle">
i want if my custom class :class is true, i should add a new class called as flexdisplay to it like
<div class="tabletitle flexdisplay">
what i am missing here, i tried passing the value to data as false but it just throwing random errors
Lets assume your parent component is like
<title :profile="true" :showClass="true"/> <!-- modified props name from class to showClass
and in your child component, as you said you have a div like below
<div class="tabletitle">
what you can do is change the above div to the below format
<div :class="['tabletitle', {'flexdisplay': enableClass}]"> <!-- where enableClass will be a computed property
Inside the script tag of your child component, define props and computed properties like below
<script>
export default {
props: {
showClass: {
type: Boolean,
default: false
},
}
computed: { // The reason I am doing with computed is for better reactivity
enableClass() {
return this.showClass;
}
}
}
</script>
You can use class condition syntax
<div :class="{ "active": isActive }"></div>
This will put the active class if the isActive condition is true
Note that you can combine the class and :class attributes to put either class conditionnaly and classic class
<div class="myClass" :class="{ "active": isActive }"></div>
For example in your case if you have the boolean class props your component will look like this :
<template>
<div class="tabletitle" :class={"flexdisplay": isClass}>
...
</div>
</template>
<script>
export default {
props: {
isClass: {
type: Boolean,
default: true
}
}
}
</script>
**Note : ** i've changed the class props by isClass to better understand and do not confuse with the class keywork
I have an application template with a sidebar component. This sidebar component is passed the application route model to display in a list. The application route checks if the user is authenticated, and if not, skips the model load. The index route is not a protected route, so it will still display when not logged in, just with no user specific data. When the user logs in or attempts to use a protected route, they are redirected to a login route and back to the attempted route transition or index.
There doesn't appear to be any way to force the application route's model hook to refresh after login. I've tried moving the data load in the application route out to a service and calling a refresh method on the service from the login route, but that resulted in the same issue.
So, my main question is what is the recommended approach to loading data after login that is needed in the application template? Is my only option to move this sidebar component to another template that is only accessible after login? This feels harder than it should be, so I am assuming I'm missing some basic concepts here with data flow between routes/components! Thanks!
My Application Route (app/routes/application.js)
import Route from "#ember/routing/route";
import { inject as service } from "#ember/service";
export default class ApplicationRoute extends Route {
#service router;
#service session;
model() {
if (this.get('session').get('isAuthenticated'))
{
return this.store.findAll("project");
}
}
}
Application Template (app/templates/application.hbs)
<HeadLayout />
<div class="wrapper">
<SideBar #projects={{this.model}} />
<div id="content">
<NavBar />
<div>
{{outlet}}
</div>
</div>
</div>
Sidebar component (app/components/side-bar.hbs)
<nav id="sidebar">
<div class="container">
<div class="sidebar-content">
...
{{#if this.session.isAuthenticated}}
<div class="sidebar-projects">
...
<div class="list-group">
{{#each this.projects as |project|}}
<button type="button">
{{project.name}}
</button>
{{/each}}
</div>
</div>
{{else}}
<p>Login to access projects.</p>
{{/if}}
</div>
</div>
</nav>
My router (app/router.js)
import EmberRouter from '#ember/routing/router';
import config from './config/environment';
export default class Router extends EmberRouter {
location = config.locationType;
rootURL = config.rootURL;
}
Router.map(function() {
this.route('login');
this.authenticatedRoute('projects', { path: '/projects'}, function() {
this.authenticatedRoute('new');
this.authenticatedRoute('edit');
this.authenticatedRoute('project', { path: ':project_id' });
});
this.authenticatedRoute('photos', { path: '/photos'}, function() {
this.authenticatedRoute('photo', {path: ':photo_id'});
});
});
You can use the authenticationSucceeded event on the session service and then call refresh. So I think this constructor for your Route can do the trick:
constructor() {
super(...arguments)
this.session.on('authenticationSucceeded', () => this.refresh());
}
This is the parent component:
<template>
<upload-block
:imSrc="LargeIcon"
inputName="LargeIcon"
:inputHandler="uploadAppIcon"
inputRef="LargeIcon"
:uploadClickHandler="handleUploadIcon"></upload-block>
</template>
<script>
export default class ParentCom extends Vue {
//all props for <upload-block></upload-block> component defined here
handleUploadIcon(event) {
const icon_type = event.currentTarget.getAttribute("data-type");
let appImgElem = this.$refs[icon_type];
appImgElem.click();
}
async uploadAppIcon(event) {
//code
}
}
</script>
And this is the child component:
<template>
<div class="upload-div" #click="uploadClickHandler" :data-type="inputName">
<img v-if="imSrc" :src="imSrc">
<div v-else class="upload-icon-block">
<span>
<font-awesome-icon class="upload-icon" icon="arrow-circle-up" size="lg"></font-awesome-icon>
<br>Click to upload
</span>
</div>
<!-- <spinner variant="primary" :show="true"></spinner> -->
<input style="display:none" type="file" :ref="inputRef" :name="inputName" #input="inputHandler">
</div>
</template>
<script>
#Component({
props: {
imSrc: String,
inputRef: String,
inputName: String,
inputHandler: Function,
uploadClickHandler: Function
}
})
export default class ChicdCom extends Vue {
}
</script>
The problem I am facing in the handleUploadIcon method in which I am not able to get the input element via ref.
It is showing Cannot read property 'click' of undefined in this line appImgElem.click();
But when I move the file input to the parent component, it's works fine. So can you plz help me how to set the ref to child component elements from parent as currently is it not setting.
Thanks
Well you could add a ref to upload-block in the parent component:
<upload-block ref="upload" ... >
Then in the handleUploadIcon you can acces your input: this.$refs.upload.$refs[icon_type]
But I would try to move handleUploadIcon to the child component if I were you.
I have a 3rd party component <third-party-component /> which accepts following event:
onAdd, onRemove, onUpdate
I want to create a wrapper component around it and want to pass these events dynamically so that can handle the response in wrapper component, something like
wrapper.js
<template>
<div class="my-wrapper">
<third-party-component />
</div>
<template>
using-wrapper.js
<template>
<div>
...
<wrapper #onAdd="add" #onRemove="remove"></wrapper>
...
</div>
</template>
<script>
export default {
methods: {
onAdd() {
console.log('on add in using-wrapper.js');
},
onRemove() {
console.log('on remove in using-wrapper.js');
}
}
}
</script>
You can pass all attributes and listeners by binding them using v-bind and v-on
You also need to set inheritAttrs to false
https://v2.vuejs.org/v2/api/#inheritAttrs
<template>
<div class="my-wrapper">
<third-party-component v-bind="$attrs" v-on="$listeners"/>
</div>
<template>
using-wrapper.js
<template>
<div>
...
<wrapper #onAdd="add" #onRemove="remove"></wrapper>
...
</div>
</template>
<script>
export default {
inheritAttrs: false,
methods: {
onAdd() {
console.log('on add in using-wrapper.js');
},
onRemove() {
console.log('on remove in using-wrapper.js');
}
}
}
</script>
Well on "wrapper" you will need to create 3 methods that listen and get triggered on "third-party-component".
<template>
<div class="my-wrapper">
<third-party-component #onAdd="wrapperAdd" #onRemove="wrapperRemove" #onUpdate="wrapperUpdate" />
</div>
<script>
export default {
methods:{
wrapperAdd(){
this.$emit("onAdd",{obj that will get to the parent});
}
}
}
I made only 1 method because the other 2 are similar.
How do I access & share variables between custom elements? I have the following files...
tip.html
<template>
<div class="tip-container">
<content select="tip-trigger"></content>
<content select="tip-content"></content>
</div>
</template>
tip.js
export class Tip {}
tip-trigger.html
<template>
<span class="tip-trigger" click.trigger="showTip()">
<content></content>
</span>
</template>
tip-trigger.js
export class TipTrigger {
showTip() {
console.debug(this);
}
}
tip-content.html
<template>
<span class="tip">
<content></content>
<span class="tip__close tip__close--default">×</span>
<span class="tip__color"></span>
</span>
</template>
tip-content.js
export class TipContent {}
In my Tip class I would like to have a variable name visible. When showTip is triggered visible would be set to true, which I would then use to add a class in tip-content.html. How can I share variables between these custom elements to do this?
The idea is to create an element to show tip pop-ups where any type of content can be the trigger and any type of content can be displayed when triggered. Basic example:
<tip>
<tip-trigger><button>?</button></tip-trigger>
<tip-content><div>Here is some helpful info...</div></tip-content>
</tip>
Here is a solution to your problem in Plunker.
Note that the tip-trigger and tip-content elements are just replaceable parts of the template. They don't needed to be components themselves (that confused me a lot in the "original" custom elements article).
app.html:
<template>
<require from="tip"></require>
<tip>
<tip-trigger><button>?</button></tip-trigger>
<tip-content><div>Here is some helpful info...</div></tip-content>
</tip>
</template>
tip.html:
<template>
<div class="tip-container">
<div>
<div click.trigger="showContent()">
<content select="tip-trigger"></content>
</div>
</div>
<div show.bind="contentVisible">
tip content:
<content select="tip-content"></content>
</div>
</div>
</template>
tip.js:
export class Tip {
showContent(){
this.contentVisible = !this.contentVisible;
}
}
Do you just need to turn Tip into a service-like class and import it?
export class Tip {
constructor() {
this.visible = false;
}
show() {
this.visible = true; // Or whatever to show the content
}
hide() {
this.visible = false;
}
}
Then:
import {inject} from 'aurelia-framework';
import {Tip} from './tip';
#inject(Tip)
export class TipTrigger {
constructor(tip) {
this.tip = tip;
}
showTip() {
this.tip.show();
// Or I suppose you could access 'visible' directly
// but I like the implementation details a method call offers.
}
}
*Disclaimer: This is untested.