Use modal on a v-for element? - html

I am getting a GET request from my api to disply all users , and i want to modify maybe the name of the email of each users with a modal. The problem is , imagine i have 3 users displayed , 3 modals will be displayed even if i press on only one modal button.
How can i do to fix it ?
there is my code :
<template>
<div class="container">
<div>
<h1>
<b> Users </b>
<input
type="text"
v-model="search"
class="search"
placeholder="Search User"
/>
</h1>
</div>
<div class="overlay-container">
<form>
<div class="form-group">
<div
v-for="user in filterUser"
v-bind:key="user"
class="form-control"
>
<p>
<b>{{ user.fname }} </b> {{ user.lname }}
</p>
<b-button v-b-modal.modal-1>Modifiy</b-button>
<b-modal id="modal-1" title="User Modification">
<p class="my-4">Please edit informations</p>
</b-modal>
</div>
</div>
</form>
</div>
</div>
</template>
<script>
import axios from "axios";
import VueAxios from "vue-axios";
export default {
name: "users",
data() {
return {
search: "",
users: [],
};
},
computed: {
filterUser() {
return this.users.filter((user) => {
return user.fname.match(this.search);
});
},
},
created() {
axios
.get(`http://localhost:4000/api/users`)
.then((Response) => (this.users = Response.data.data));
},
};
</script>
I want to each users open his own modal, not all modals when i press only one modal button.
I hope its clear !

First, pull out the modal inside of v-for, then create a method to get your user index, and add it to the Modify button. this is how you can get the data a user without sending another HTTP Request. and then you can show this.user data in your modal as you want.
<div
v-for="(user, index) in filterUser"
v-bind:key="user"
class="form-control"
>
<p>
<b>{{ user.fname }} </b> {{ user.lname }}
</p>
<b-button
v-b-modal.modal-1
#click="getUserInfo(index)"
>
Modifiy
</b-button>
</div>
data() {
return {
search: "",
users: [],
user: {}
};
},
methods: {
getUserInfo(index) {
this.user = this.users[index]
}
}

Related

Alpine JS fetch data - limit x-for iteration results and store data for later use

Alpine JS fetch data
How we should do to limit x-for iteration (like the json have 10 results but i want to show only five) and store data for later use with another script outside like a slider to add data ater each slide.
In short, retrieve the json response data to load the next slider image only when the slider arrow will be clicked or the slider will be swiped.
The data should be stored for use in javascript.
HTML:
<div class="main" x-data="init()">
<h4 class="font-xxlarge">Movie search in Alpine.js</h4>
<div class="searchArea">
<input
class="inputText"
type="text"
placeholder="Type to search a fact"
x-model="q"
#keyup.enter="search()"
/>
<button class="bg-default" #click="search()">Search</button>
<br><br>
</div>
<div>
<template x-for="result in results">
<div class="movieCard">
<div>
<img x-bind:src="result.Poster" />
</div>
<div>
<div class="movieDetailItem">
<span style="padding-right: 5px">Title:</span
><span><b x-text="result.Title">Man of Steel</b></span>
</div>
<div class="movieDetailItem">
<span style="padding-right: 5px">Year:</span
><span><b x-text="result.Year">2008</b></span>
</div>
</div>
</div>
</template>
JS:
function init() {
return {
results: [],
q: "",
search: function () {
fetch(
"https://www.omdbapi.com/?&apikey=e1a73560&s=" + this.q + "&type=movie"
)
.then((response) => response.json())
.then((response) => (this.results = response.Search))
.then(response => console.log(response))
.catch((err) => console.log(err));
// console.log(response);
},
};
}
Codepen example: https://codepen.io/onigetoc/pen/yLKXwQa
Alpine.js calls this feature getters, they return data based on other states. Let's say we have startIndex and endIndex variables, then we can do a simple filtering with filter() in the getter method, that returns the items between these two indexes.
<script src="https://unpkg.com/alpinejs#3.x.x/dist/cdn.min.js"></script>
<script>
function init() {
return {
results: ['#1', '#2', '#3', '#4', '#5'],
startIndex: 2,
endIndex: 4,
get filteredResults() {
return this.results.filter((val, index) => {
return index >= this.startIndex && index <= this.endIndex
})
}
}
}
</script>
<div class="main" x-data="init()">
Items:<br>
<template x-for="result in results">
<span x-text="`${result} `"></span>
</template>
<br><br>
Filtered items between index: <span x-text="`${startIndex} and ${endIndex}`"></span>:<br>
<template x-for="result in filteredResults">
<span x-text="`${result} `"></span>
</template>
</div>

How to display data from multiple tables in vue js and Laravel?

I'm trying to display data from three tables in my database on one page. I've managed to do it, everything is working the way I want but I have an error: Error in render: "TypeError: Cannot read property 'file' of undefined"
It seems like I'm getting this error when trying to retrieve the videos data WITHOUT using a v-for loop.
Is it possible to display data on a page without using a v-for loop?
Here is my code.
CandidateProfileController.php:
public function index()
{
$videos = Video::all();
$resumes = Resume::all();
$profile = CandidateProfile::all();
return Response::json(array(
'videos' => $videos,
'resumes' => $resumes,
'profiles' => $profile
));
}
CandidateProfileIndex.vue:
<template>
<div class="col-md-12 grid-margin stretch-card">
<div class="card">
<div class="card-body">
<h3 class="card-title">My Profile</h3>
<b-container class="bv-example-row">
<b-row>
<b-embed
type="video"
aspect="16by9"
:src="`${$store.state.serverPath}/storage/videos/${videos[0].file}`"
allowfullscreen
controls
></b-embed>
</b-row> <br>
<b-row>
<b-col class="text-right" cols="8"></b-col>
<b-col class="text-right" cols="2">
<b-embed
type="video"
aspect="16by9"
:src="`${$store.state.serverPath}/storage/videos/${videos[1].file}`"
controls
class="video-thumbnail"
></b-embed>
</b-col>
<b-col class="text-right" cols="2">
<b-embed
type="video"
aspect="16by9"
:src="`${$store.state.serverPath}/storage/videos/${videos[2].file}`"
controls
class="video-thumbnail"
></b-embed>
</b-col>
</b-row>
</b-container>
<br>
<b-container v-for="(profile, index) in profiles" :key="index">
<div class="b-row">
<div class="b-col" v-for="(resume, index) in resumes" :key="index">
<h4 style="float: left;">Resume:</h4>
<span style="font-size: 0.88rem;">{{resume.file}}</span><br><br>
</div>
</div>
<div class="b-row">
<div class="b-col">
<h4>Experience</h4>
<p>{{profile.experience}}</p>
</div>
</div>
<div class="b-row">
<div class="b-col">
<h4>Additional Skills</h4>
<p>{{profile.skills}}</p>
</div>
</div>
</b-container>
</div>
</div>
</div>
</template>
<script>
import * as groupedService from '../../services/grouped_data_service.js';
export default {
name: "candidateProfileIndex",
data() {
return {
profiles: [],
videos: [],
resumes: [],
};
},
mounted() {
this.loadGroupedData();
},
methods: {
loadGroupedData: async function() {
try {
const response = await groupedService.loadGroupedData();
console.log(response);
this.resumes = response.data.resumes;
this.videos = response.data.videos;
this.profiles = response.data.profiles;
console.log(this.resumes);
} catch (error) {
this.$toast.error("Some error occurred, please refresh!");
}
}
}
}
</script>
grouped_data_service.js:
import {http, httpFile} from './http_service';
export function loadGroupedData() {
return http().get('/candidate-profile');
}

Angular Submit by pressing Enter key or click button

I would like that when i press the enter key or the log in button to submit. Only the click on the button is working at the time of submitting.
Can anyone help me get both of them working?
<div class="BLog">
<div class="Log" (click)="Log()">
Log in
</div>
</div>
async Log() {
let self = this;
let ident = {
username: self.username,
password: self.password,
client_id: "task",
grant_type: "password",
}
let aux = await self.logService.postAt(ident);
if(aux) self.router.navigate(['first']);
}
You have to wire this up in a form and add a button of type submit.
<form (submit)="Log()">
<div class="BLog">
<div class="Log">
<button type="submit">Log in</button>
</div>
</div>
</form>
If you want to do it without form,
https://stackblitz.com/edit/angular-4k2xr4
export class AppComponent {
name = 'Angular';
Log() {
this.name = "Logged In"
}
handleKeyboardEvent(event) {
if (event.keyCode === 13) {
this.Log()
}
}
}
<hello name="{{ name }}"></hello>
<div class="BLog" (keydown)="handleKeyboardEvent($event)">
<input value="type and press enter">
<div class="Log">
Log in
</div>
</div>

Why does using *ngIf = "authService.getUser()" in my top level app component html break my login component?

I am new to Angular so please bear with my naivety. I created a login component, which I use to prevent access to router navigation links in the main app-component html until the user logs in. The login component itself is another routed page, and I wanted to instead add the login component to my mainpage, and hide the routing navigation links until the user logs in.
To hide the user navigation links in the app-component html I tried using
*ngIf="authService.getUser()".
*ngIf="authService.getUser()" hides the navigation components until the user is logged in as expected, but it unexpectedly also didn't render the entirety of the login component (the user and password fields and submit button), except the first text which simply says "LOGIN". So the user has no way to login.
this is app.component.html:
<app-login>
</app-login>
<div class="page-header" >
<div class="container">
<div *ngIf="authService.getUser()" class="navLinks">
<a [routerLink]="['/home']"> Home</a>
<a [routerLink]="['/about']"> About App</a>
<a [routerLink]="['/login']">Login</a>
<a [routerLink]="['/protected']">Protected</a>
</div>
</div>
</div>
<div id="content">
<div class="container">
<router-outlet></router-outlet>
</div>
</div>
and these are my login component files
login.component.ts:
import { Component } from '#angular/core';
import { AuthService } from '../auth.service';
#Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.css']
})
export class LoginComponent {
message: string;
constructor(public authService: AuthService) {
this.message = '';
}
login(username: string, password: string): boolean {
this.message = '';
if (!this.authService.login(username, password)) {
this.message = 'Incorrect credentials.';
setTimeout(function() {
this.message = '';
}.bind(this), 2500);
}
return false;
}
logout(): boolean {
this.authService.logout();
return false;
}
}
login.component.html:
<h1>Login</h1>
<div class="alert alert-danger" role="alert" *ngIf="message">
{{ message }}
</div>
<form class="form-inline" *ngIf="!authService.getUser()">
<div class="form-group">
<label for="username">User: (type <em>user</em>)</label>
<input class="form-control" name="username" #username>
</div>
<div class="form-group">
<label for="password">Password: (type <em>password</em>)</label>
<input class="form-control" type="password" name="password" #password>
</div>
<a class="btn btn-default" (click)="login(username.value, password.value)">
Submit
</a>
</form>
<div class="well" *ngIf="authService.getUser()">
Logged in as <b>{{ authService.getUser() }}</b>
<a href (click)="logout()">Log out</a>
</div>
What is in your authService.getUser() method? If it is asynchronous all you have to do is *ngIf="(authService.getUser() | async)".
I don't understand why are you using *ngIf="!authService.getUser()" inside your login component like this.
Semantically the purpose of this component is to be used when you're not logged.
So simply try to wrap your <app-login></app-login> in your app.component.html into a div wich have the *ngIf="!authService.getUser()"
Like this :
<div *ngIf="!authService.getUser()">
<app-login></app-login>
</div>
Also I recommand you to not use directly the service method like this in the html but a flag instead for example isLogged init to false and update it when the user successfully logged.
Your ng-if will be : *ngIf="!isLogged | async"

Post method doesn't work with html form in MVC

Why is that the $.post() method doesn't work ? I press the button but it doesn't post to EditClassLessonHour.
Here is the View Code:
<div class="jumbotron">
<form id="editClassLessonHour">
#Html.HiddenFor(model => model.ID)
<span> Classes : </span>
<select name="classes" id="classes">
#foreach (var item in ViewBag.classes as List<SelectListItem>)
{
<option value="#item.Value">#item.Text</option>
}
</select>
<br /><br />
<div class="input-group">
<span class="input-group-addon" name="lessons" id="lessons">Lessons : </span>
#Html.DropDownListFor(x => x.LessonName, (IEnumerable<SelectListItem>)ViewData["lesson"], new { #class = "form-control", placeholder = "Lesson", aria_describedby = "txtClassNumber" })
#Html.ValidationMessageFor(model => model.LessonName)
</div>
<br />
<div class="input-group">
<span class="input-group-addon" name="hours" id="hours">Hour : </span>
#Html.DropDownListFor(x => x.Hour, (IEnumerable<SelectListItem>)ViewData["hour"], new { #class = "form-control", placeholder = "Lesson", aria_describedby = "txtClassNumber" })
#Html.ValidationMessageFor(model => model.Hour)
</div>
<br />
<input type="button" value="Kaydet" id="kaydet" class="btn btn-success" onclick="post(); " />
</form>
<br />
</div>
<script>
$.post('/ClassLessonHour/EditClassLessonHour',{
classId: $('#classes').val(),
lessonId: $('#lessons').val(),
hourId: $('#hours').val()
}, function (result) {
$('#message').html(result.message);
});
</script>
Here is Controller Code
[HttpPost]
public ActionResult EditClassLessonHour(int classId, int lessonId, int hourId)
{
}
What can I do? I want that when I press the button, the form should be posted to the Controller.
onclick="post();"
You don't have a function called post. You don't have any function at all. You're just executing an AJAX POST request as soon as the page loads.
Remove that onclick attribute entirely and bind your AJAX call to the button's click event. Something like this:
$(function () {
$('#kaydet').on('click', function () {
$.post('/ClassLessonHour/EditClassLessonHour',{
classId: $('#classes').val(),
lessonId: $('#lessons').val(),
hourId: $('#hours').val()
}, function (result) {
$('#message').html(result.message);
});
});
});
Though it seems likely that there are more problems here. For example, this:
$('#lessons').val()
The id="lessons" element is a <span>, which doesn't have a "value". You want to target the <select> being generated by #Html.DropDownListFor, not the <span> that's near it. Examine your HTML in the browser to see what is being generated by that server-side code. It may have an id or name that you can use in your jQuery selector. (This same problem applies to your #hours element as well.)