Vue.js Display call one component from another component - javascript

I have 2 components:
Vue.component('repo-button', {
props:["check_in_id", "repo_id"],
template: '#repo-button',
methods: {
fetchRepo: function() {
url = window.location.href.split("#")[0] + "/check_ins/" + this.check_in_id + "/repositionings/" + this.repo_id + ".json"
cl(url)
cl(this)
var that;
that = this;
$.ajax({
url: url,
success: function(data) {
cl(data)
that.showRepo();
}
})
},
showRepo: function() {
// what do I put here to display the modal
}
},
data: function() {
var that = this;
return {
}
}
});
Vue.component('repo-modal', {
template: "#repo-modal",
data: function() {
return {
status: 'none'
}
}
});
var repositionings = new Vue({
el: "#repo-vue"
});
...and my view consists of a button and a modal. I'd like the button to call fetchRepo on the repo-button component and display the modal (change its status property from none to block.
<script type="text/x-template" id="repo-button">
<div class='socialCircle-item success'>
<i class='fa fa-comment'
#click="fetchRepo"
:data-check_in='check_in_id'
:data-repo='repo_id'>
</i>
</div>
</script>
<script type="text/x-template" id="repo-modal">
<div v-bind:style="{ display: status }" class="modal" id="vue-modal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true" data-client_id="<%= #client.id %>">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title"></h4>
</div>
<div class="modal-body"></div>
<div class="modal-footer">
<button type="button" class="btn btn-danger btn-simple" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
</script>
<div id="repo-vue">
<div is="repo-modal"></div>
<div is="repo-button" repo_id="<%= ci.repositioning.id %>" check_in_id="<%= ci.id %>"></div>
</div>

Props down, events up
In Vue.js, the parent-child component relationship can be summarized
as props down, events up. The parent passes data down to the child via
props, and the child sends messages to the parent via events.
In particular, if the state of a component needs to be controlled externally (by a parent or sibling), that state should be passed in as a prop from the parent. Events indicate to the parent that the state should be changed.
Your modal's state is controlled by events in itself and in a sibling component. So the state lives in the parent, and is passed to the modal as a prop. Clicking the modal Close button emits a (custom) hidemodal event; clicking the sibling component's comment icon emits a showmodal event. The parent handles those events by setting its showRepoModal data item accordingly.
Vue.component('repo-button', {
template: '#repo-button',
methods: {
showRepo: function() {
this.$emit('showmodal');
}
}
});
Vue.component('repo-modal', {
template: "#repo-modal",
props: ["show"],
computed: {
status() {
return this.show ? 'block' : 'none'
}
},
methods: {
hideRepo() {
this.$emit('hidemodal');
}
}
});
var repositionings = new Vue({
el: "#repo-vue",
data: {
showRepoModal: false
}
});
.socialCircle-item i {
cursor: pointer;
}
<link href="//cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" />
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.2.6/vue.min.js"></script>
<template id="repo-button">
<div class='socialCircle-item success'>
<i class='fa fa-comment'
#click="showRepo"
>
</i>
</div>
</template>
<template id="repo-modal">
<div v-bind:style="{ display: status }" class="modal" id="vue-modal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true" >
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title"></h4>
</div>
<div class="modal-body"></div>
<div class="modal-footer">
<button type="button" #click="hideRepo" class="btn btn-danger btn-simple" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
</template>
<div id="repo-vue">
<div is="repo-modal" :show="showRepoModal" #hidemodal="showRepoModal = false"></div>
<div is="repo-button" #showmodal="showRepoModal = true"></div>
</div>

Related

Vuejs 3 - Problem with data function inside a component

I have a list of projects which I get from an api. They get forwarded one by one to this component:
<template>
<button id="project-button" #contextmenu="handler($event)" class="btn btn-outline-secondary mt-1 ms-1" data-bs-toggle="collapse" :data-bs-target="`#${project.Name + project.ID}`">{{project.Name}}</button>
<div :id="`${project.Name + project.ID}`" class="collapse">
<button #click="setProjectId" id="add-object-button" class="btn btn-outline-secondary mt-1 ms-1" data-bs-toggle="modal" data-bs-target="#createObject">
<b>Add Data Object</b>
<i id="plus-icon" class="fas fa-plus"></i>
</button>
<DataObjects :dataObjects="dataobjects"/>
</div>
<CreateObjectModal #add-object="addObject" :projectId="projectId"/>
</template>
<script>
import DataObjects from './DataObjects'
import CreateObjectModal from './CreateObjectModal'
export default {
name: 'Project',
data() {
return {
dataobjects: [],
projectId: 0,
}
},
props: {
project: Object,
},
components: {
DataObjects,
CreateObjectModal
},
methods: {
setProjectId() {
console.log(this.project.ID)
this.projectId = this.project.ID
console.log(this.projectId)
},
async addObject(objectName) {
console.log(objectName)
console.log("After adding object: " + this.projectId)
console.log("Event arrived")
},
Inside the data function I have an object which has the attribute projectId. When I click on the Add Data Object button a modal opens where the user can type in the name of the data object. The modal opens through the data-bs-toggle and data-bs-target attributes. Now in my click event for this button I can get the individual id of a project depending on which button I click. But the problem here is that I will need the specific id of a project inside my addObject function. The addObject function gets called when I click 'Ok' in my modal after the user types in a name. The logs show clearly that if I click on a button the different id gets set correctly but when I click 'Ok' from the modal and the AddObject function gets called then the id remains 1 which is the first id of the projects
I dont know if I am complicating things but I tried to send the id to my modal component which didnt work. The idea was to send it back to the addObject function via $emit. I am kinda stuck with this problem and hope someone can help me. Here is the Modal Component:
<template>
<div>
<div class="modal fade" id="createObject" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">New Data Object</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="form-floating mb-3">
<input v-model="objectName" class="form-control" id="input">
<label for="input">Object Name:</label>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<Button data-bs-dismiss="modal" #click="createObject" :text="'Save'"/>
<p>{{projectId}}</p>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import Button from './Button'
export default {
name: 'CreateObjectModal',
data() {
return {
objectName: '',
}
},
props:{
projectId: Number
},
methods: {
createObject() {
this.$emit('add-object', this.objectName)
}
},
components: {
Button
},
emits: ['add-object']
}
</script>

How to use a loop inside a slick slider in Vue.js

I'm using vue-slick link https://www.npmjs.com/package/vue-slick inside a bootstrap modal and when i use a v-for loop to loop through the items it display's this.
Here's a snippet of my code
import Slick from 'vue-slick';
export default {
props: ['vehicleRequest'],
components: {'slick': Slick},
data() {
return {
vehicle: {
model: '',
licensePlate: '',
type: '',
currentMileage: '',
Availability: '',
imageUrl: ''
},
vehicles: [],
slickOptions: {
infinite: true,
slidesToShow: 5,
slidesToScroll: 3,
},
}
},
mounted() {
console.log("vehicleRequestId: " + JSON.stringify(this.vehicleRequest));
},
methods: {
updated() {
this.reInit();
},
reInit() {
this.$refs.slickone.reSlick();
},
reserveVehicle() {},
allocateVehicle() {},
getVehicleRequest(id) {},
approveOnline() {
console.log("approve online!");
},
approveOffline() {
console.log("approve offline!");
},
declineRequest() {
$('#declineRequest-Modal').modal('hide');
console.log("vehiclerequest: " + this.vehicleRequest);
console.log("declined");
},
viewVehicle(vehicles) {
let self = this;
self.vehicles = vehicles
$('#viewVehicle').modal('show');
}
}
}
<div
class="modal fade"
id="viewVehicle"
tabindex="-1"
role="dialog"
aria-labelledby="exampleModalCenterTitle"
aria-hidden="true"
>
<div class="modal-dialog modal-lg modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalCenterTitle">{{vehicle.licensePlate}}</h5>
<button
type="button"
class="close"
data-dismiss="modal"
aria-label="Close"
>
<i
class="fas fa-times-circle"
aria-hidden="true"
style="font-size:20px;color: #6e6f6c;"
></i>
</button>
</div>
<div class="modal-body">
<slick ref="slickone" :options="slickOptions">
<div v-for='vehicle in vehicles' :key="vehicle.id">
<img :src="vehicle.vehicle_image_url">
</div>
</slick>
</div>
<div class="col-md-10 mx-auto mb-4">
<div class="row">
<div class="col-md-6">
<div class="w-100">
<button
type="button"
style="margin-left: 25%; padding-left: 20px; padding-right: 20px;"
id="alternativeVehicle"
#click="allocateVehicle()"
class="unicef-btn unicef-reset small-button"
>ALLOCATE ALTERNATIVE VEHICLE</button>
</div>
</div>
<div class="col-md-6">
<div class="w-100">
<button
type="button"
style="margin-left: -7%;"
#click="reserveVehicle()"
id="reserve"
class="unicef-btn unicef-primary small-button"
>RESERVE</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
What could be the problem ?
Sorry guys I was too busy that I hadn't got time to post the above solution anyway I've figured a way to do it although the css styling needs polishing but it still solves what I wanted. What I did was to create property in data called currentIndex and also a function called current vehicle in the computed property. Then I looped the [currentIndex] to get the currentVehicle
computed:{
currentVehicle: function(){
return this.vehicles[Math.abs(this.currentIndex) % this.vehicles.length];
},
},
So the currentVehicle() returns the index of the vehicle in view.
Here's a working one
https://jsfiddle.net/nelsonk/ka01zvtm/

How to use Vue.js to template a Modal and reuse it?

So currently I have a web app that loads a dashboard based on a SQL database and that works just fine, each of these elements have buttons and those buttons are SUPPOSED to spawn a modal with the content of the problem and you can interact with it etc.
Recently I have figured out how to use Vue.js to run a function that grabs this information and populate it on the modal. However, this modal can only be templated ONCE before it wont work again. Ex: I can click on problem 1 and problem 1's information is correct. however, if I click problem 2 after that it doesn't work
new Vue({
delimiters: ['!!!','!!!'],
el: '#problem-modal',
methods: {
close() {
this.$emit('close');
},
},
data: {
items: [],
name: null,
summary: null,
},
mounted: function () {
var self = this;
fetch('./api/problems')
.then(response => {
var response = response.json();
return response;
})
.then(json => {
var data = json.data;
var length = data.length;
for (var i = 0; i<length; i++) {
if (data[i].unique_id == button_id) {
this.name = data[i].problem_name;
this.summary = data[i].summary;
}
}
})
}
})
}
Here is my Script for the vue, assume that the problems load perfectly and this:
<div class="modal fade" id="problem-modal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">!!! name !!!</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
!!! summary !!!
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary">Save changes</button>
</div>
</div>
</div>
</div>
is the modal. Any idea on how to make the modal element able to be used more than once?
Thanks!
I give you nearest illustration of your question ! I thought items is your problem list . As you mentioned you have to clicked button so that pass your clicked data to call method and grab data from items with that pass clicked data and set vue data . I hope it will be ok .. Look at my example
new Vue({
el: '#problem-modal',
methods: {
show(id) {
let cur = this.items.find(x => x.id == id);
this.name = cur.name;
this.summary = cur.summary;
}
},
data: {
items: [
{
id: 1,
name: "one",
summary: "one summary "
},
{
id: 2,
name: "two",
summary : "two summary"
}
],
name: null,
summary: null,
}
});
button {
width: 100%;
height: 20px;
margin: 10px 5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="problem-modal">
<button v-for="item in items" #click="show(item.id)">{{item.id}}</button>
<div class="modal">
<h1>{{name}}</h1>
<pre>{{summary}}</pre>
</div>
</div>

Set focus on a input control contained in a second level bootstrap modal

I'm using Vue.js 2.1.10 and Bootstrap 3.3.7 to show a modal that opens another modal dialog. Each modal dialog is contained in a distinct component. Inside the 1st component, there is a reference to the 2nd component (select-travler).
According to the Bootsrap documentation, I have to set the focus by listening to the event shown.bs.modal. This works great to set the focus on an input control contained in the 1st modal. Problem: this way doesn't work when the modal is above another modal.
The 1st modal component looks like this:
<template>
<div ref="tripEdit" class="modal fade" role="dialog">
<!-- Inbeded component -->
<select-travler ref="selectTravler"></select-travler>
<!-- /Inbeded component -->
<div class="modal-lg modal-dialog">
<div class="modal-content">
<div class="modal-body container form-horizontal">
<div class="form-group">
<label for="travler_name" class="control-label">
Travler's name
</label>
<input id="travler_name" ref="travler_name"
v-model="travler_name"/>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
data () {
return {
travler_name: null,
}
},
methods: {
show (operationType) {
$(this.$refs.tripEdit).modal('show');
let that = this;
$(this.$refs.tripEdit).on('shown.bs.modal', function () {
$(that.$refs.travler_name).focus();
});
if (operationType === 'newTravel') {
this.$refs.selectTravler.show();
}
},
},
}
</script>
The 2nd component contains a similar layout with the following show method:
show () {
$(this.$refs.selectTravler).modal('show');
let that = this;
$(this.$refs.selectTravler).on('shown.bs.modal', function () {
$(that.$refs.people_names).focus();
});
},
When the 2nd modal opens, the focus is still on the 1st modal behind the 2nd modal dialog (I can see the caret blinking in travler_name). How can I set the focus on people_names when the 2nd modal is shown?
I think there are really several issues at play here. First, as I mentioned in the comment above, you are not properly adding and removing the shown.bs.modal event handlers.
Second, because your second modal is nested inside the first modal, the shown.bs.modal event will bubble up to the parent modal and it's handler will fire. Initially I thought stopPropagation would be a good way to handle this, but in the end, I simply de-nested the submodal component in the template.
Here is an example of this behavior actually working.
console.clear()
Vue.component("sub-modal", {
template: "#submodal",
methods: {
show() {
$(this.$el).modal("show")
},
onShown(event) {
console.log("submodal onshown")
this.$refs.input.focus()
}
},
mounted() {
$(this.$el).on("shown.bs.modal", this.onShown)
},
beforeDestroy() {
$(this.$el).off("shown.bs.modal", this.onShown)
}
})
Vue.component("modal", {
template: "#modal",
methods: {
show() {
$(this.$refs.modal).modal("show")
},
showSubModal() {
this.$refs.submodal.show()
},
onShown(event) {
console.log("parent")
this.$refs.input.focus()
}
},
mounted() {
$(this.$refs.modal).on("shown.bs.modal", this.onShown)
},
beforeDestroy() {
$(this.$refs.modal).off("shown.bs.modal", this.onShown)
}
})
new Vue({
el: "#app",
})
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://unpkg.com/vue#2.2.6/dist/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
<div id="app">
<modal ref="modal"></modal>
<button #click="$refs.modal.show()" class="btn">Show Modal</button>
</div>
<template id="submodal">
<div class="modal fade" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h4 class="modal-title">Modal title</h4>
</div>
<div class="modal-body">
<input ref="input" type="text" class="form-control">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary">Save changes</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
</template>
<template id="modal">
<div>
<div ref="modal" class="modal fade" tabindex="-1" role="dialog">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h4 class="modal-title">Modal title</h4>
</div>
<div class="modal-body">
Stuff
<input ref="input" type="text" class="form-control">
</div>
<div class="modal-footer">
<button #click="showSubModal" type="button" class="btn btn-primary">Show Sub Modal</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
<sub-modal ref="submodal"></sub-modal>
</div>
</template>
Also, for future readers, I got some useful information about how to construct the template for the modal component used above from this answer. Specifically, unless you manually specify a z-index for the modal, the modal that appears last in HTML will have a higher z-index. The implication being the submodal component needs to come second in the template.
I ran into a similar issue. A b-modal forces focus to stay in the modal. You can disable it by adding a no-enforce-focus attribute.
no-enforce-focus Boolean false Disables the enforce focus routine
which maintains focus inside the modal
https://bootstrap-vue.org/docs/components/modal
This means that the element you're trying to focus is not properly referenced.
Trying to console.log(element); the line before focussing people_names. To see if you're getting the right element.
show () {
$(this.$refs.selectTravler).modal('show');
let element = this.$refs.people_names;
$(this.$refs.selectTravler).on('shown.bs.modal', function () {
$(element).focus();
});
},
Have you considered v-show to open and close your modals ?

Binding data to and retrieving data from DOM elements in Meteor 1.2

I am retrieving friend objects via facebook graph API. The idea is to display a list of the names of returned friends, allow user to select one or more friends from this list, and determine the IDs of the friends selected by user once a button is clicked.
So far, I have the following code...
detail.js:
Template.detail.helpers({
...
listOfFriends: function() {
var list = new Array();
if (Meteor.user()) {
list = Meteor.user().profile.listOfFriends;
}
return list;
},
...
});
Template.detail.events({
...
'click .select-friends': function(e) {
e.preventDefault();
// Display modal.
$('#friend_list_modal').modal('show');
Meteor.call('getFacebookFriends', function(err, response) {
if (err) {
alert(JSON.stringify(err));
} else {
if (response.statusCode != 200) {
alert("Error: " + response.statusCode);
}
}
});
},
'click #get_ids_button': function(e) {
e.preventDefault();
var checkedFriendNames = {}, counter = 0;
$("#friend_list li.active").each(function(idx, li) {
checkedFriendNames[counter] = $(li).text();
counter++;
});
// At this point, I have a list of names in checkedFriendNames,
// but I want the IDs and maybe other attributes as well.
$('#friend_list_modal').modal('hide');
}
});
detail.html:
<template name="detail">
<div class="page page-detail">
...
<div class="container">
<div class="btn btn-danger list-friends pull-right" data-toggle="tooltip" title="list friends">
<span class="glyphicon glyphicon-user"></span>
</div>
</div>
</div>
<!-- Modal -->
<div id="friend_list_modal" class="modal fade" role="dialog">
<div class="modal-dialog">
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h4 class="modal-title">List of Friends</h4>
</div>
<div class="modal-body">
<div>
<div style="max-height: 300px; overflow: auto;">
<ul id="friend_list" class="list-group checked-list-box">
{{#each listOfFriends}}
<li class="list-group-item">{{name}}</li>
{{/each}}
</ul>
</div>
</div>
</div>
<div class="modal-footer">
<button id="get_ids_button" type="button" class="btn btn-default">Get IDs</button>
</div>
</div>
</div>
</div>
</template>
server.js:
Meteor.methods({
...
getFacebookFriends: function() {
this.unblock();
var graphResponse = Meteor.http.call("GET", "https://graph.facebook.com/v2.5/me/friends", {
params: {
access_token: Meteor.user().services.facebook.accessToken
}
});
// Save user's list of friends.
Meteor.users.update(Meteor.userId(), {$set: {"profile.listOfFriends": graphResponse.data.data}});
return graphResponse;
},
...
});
What is the best way, in Meteor, to bind the friend object (with id, name, ... attributes) to the DOM and then get these attributes, such as friend ID, back once a selection is made by user?
I'm not sure if Meteor makes this any easier, but you could use data attributes to store extra information about your friend objects in the HTML element.
To save:
<li class="list-group-item" data-friend-id="{{id}}">{{name}}</li>
To retrieve:
var friendId = $(li).data('friendId');

Categories

Resources