Blazor, Image from Graph API not loading without refreshing - javascript

In my Blazor app when I user signs in (through Azure AD) I am using an async Graph API call to retrieve their profile picture and display it in the top bar.
The problem is it only loads after a window refresh.
To try to load it after I re-run the async method when they click login, since the OnInitializedAsync() runs before they sign in as well then, the loginDisplay view is first rendered.
after converting the picture to a Base64String I then check if the string is not empty (result of null or a failed conversion for the method I wrote) and try o display the image:
#if (userPhoto != string.Empty)
{
<div class="col-md-auto align-self-center position-relative">
<a #onclick="() => userMenu()">
<img id="mePicture" class="rounded-circle" src="#userPhoto" style="width: 45px; height: 45px;" />
</a>
#if (renderUserMenu)
{
<div class="container-fluid position-absolute top-0 end-50 border-1 p-2 border-dark shadow bg-light rounded bg-dark" style="width: 120px;" #onblur="() => userMenu()">
<button class="btn btn-sm btn-outline-secondary m-0 p-0 shadow-sm" style="height: 20px; width: 20px;" #onclick="() => userMenu()">X</button>
<div class="text-center align-content-center">
<img class="rounded-circle" src="#userPhoto" style="width: 65px; height: 65px;"/>
<button class="btn btn-sm btn-outline-secondary shadow-sm" #onclick="BeginLogout">Log out</button>
</div>
</div>
}
</div>
}
else
{
<div class="col-md-auto align-self-center">
<div class="rounded-circle" style="width: 45px; height: 45px;"> #initials</div>
<img class="rounded-circle" src="#userPhoto" style="width: 45px; height: 45px;" />
</div>
}
I don't know how to force it to render after the async method fetches it.
My guess is that the LodingDisplay.razor partial doesn't refresh after login in.
I tried using JS to load it (didn't fail, but didn't render it)
Also tried a secondary function on the #onclick event of the login button also to no effect.
Any help appreciated.
Relevant C# Code:
protected override async Task OnInitializedAsync()
{
var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
var _user = authState.User;
if (_user.Identity.IsAuthenticated)
{
claims = _user.Claims;
var request = GraphClient.Me.Request();
user = await request.GetAsync();
await GetMePhotoAsync();
authMessage = "";
}
}
private async Task GetMePhotoAsync()
{
var photo = await GraphClient.Me.Photo.Content.Request().GetAsync();
byte[] bytes;
using (var memoryStream = new MemoryStream())
{
photo.CopyTo(memoryStream);
bytes = memoryStream.ToArray();
}
var photoString = Convert.ToBase64String(bytes);
userPhoto = string.Format("data:image/jpeg;base64,{0}", photoString);
var uid = user.Id;
var displayName = user.DisplayName;
var displayNameList = displayName.Split(" ").ToList();
if (displayNameList.Count > 1)
{
initials = displayNameList[0].Substring(0) + displayNameList[1].Substring(0);
}
else
{
initials = displayName.Substring(0, 1);
}
}

Related

Vue.js does not display value on html

I really don't know what to do about it.
I'll try to explain:
I need to output value "senderName" in block of chat. For it, iteration over the arrays is used on mounted method. There are successfully output in console and Vue debagger extension, but the field for name is empty.If you enter text instead of the {{}} template, then everything is displayed.
This is html code
<div
v-for="chat in chats"
:key="chat.id"
#click="changeChat(chat.private_chat_id)"
class="card chat-chart"
style="
width: 100%;
margin-bottom: 1px;
box-shadow: none;
border: 1px solid #eee;
"
>
<div
class="card-body mt--3"
style="
height: 100px !important;
padding: 0.8rem i !important;
"
>
<div class="row align-items-center justify-content-start">
<div class="col-2">
<span class="avatar avatar-sm rounded-circle">
<img
src="https://ptetutorials.com/images/user-profile.png"
alt="avatar"
/>
</span>
</div>
<div class="col-8 d-flex align-items-center">
<h4 class="card-title align-self-center mb-0">
{{ chat.senderName }}
</h4>
</div>
</div>
<div
class="row align-items-center justify-content-end mt--4 mb-4"
style="height: 1px"
>
<div
v-if="chat.unreadCount > 0"
class="d-inline-block rounded-circle bg-success text-light ml-2 p-1"
style="
color: white !important;
font-weight: bold;
width: 30px;
height: 30px;
"
>
<!-- <p
class="text-center font-weight-bold"
style="line-height: 1.4"
>
{{ (chat.unreadCount > 99) ? 99 : chat.unreadCount
}}
</p> -->
</div>
</div>
<div
class="row align-items-center justify-content-start ml-1"
>
<p>chat.msg</p>
</div>
</div>
</div>
And here mounted method
mounted() {
// Get all chats for user with id - userId
axios
.get("/api/users/" + this.userId + "/privatechat")
.then((res) => {
appChats.chats = res.data;
// console.log(this.chats);
// Itereate array of user's private chats
for (let i = 0; i <= res.data.length; i++) {
if (!res.data[i]) {
continue;
} else {
// console.log(res.data[i]);
let chatObj = res.data[i];
// Get info of the chat with its id
axios
.get("/api/privatechat/" + chatObj.private_chat_id)
.then((response) => {
// console.log(response.data);
for (values of response.data) {
// If user's id of the chat equals id of our user then skip
if (values.user_id == appChats.userId) continue;
else {
// console.log(values.user_id);
appChats.chats[i]["senderId"] = values.user_id;
// Get userName of our interlocutor
axios
.get("/api/users/" + values.user_id)
.then((r) => {
appChats.chats[i]["senderName"] =
r.data[0].username;
});
}
}
});
}
}
});
},
There are some pics:
https://i.imgur.com/tSKBIeK.png
https://i.imgur.com/nxbhOvE.png // this pic is about Vue js extension

How can I print a specific Index of an Array on the DOM after a button click redirecting to a different HTML file?

I am using a Bootstrap Carousel, but the slides of the carousel are generated through DOM by looping through an array called "Capris".
There is a button that displays on each slide that is also specific to each index.
What I am trying to do is when a User clicks on one of the buttons, it redirects them to a different HTML file and prints to DOM that same index, just on the other HTML page.
I managed to do everything except having that index print to DOM on the second HTML file. Here is a snippet of my code for the carousel and the button's event listener:
const buyCapriBttn = () => {
for (let i = 0; i < capris.length; i++) {
document.querySelector(`#${capris[i].name}`).addEventListener('click', function () {generateProduct(capris[i])});
}
}
const capriCarousel = () => {
let domString = '';
for (let i = 0; i < capris.length; i++) {
if (i === 0) {
domString += `
<div class="carousel-item active">
<img class="d-block w-100" src="${capris[i].imageUrl}" alt="Capri 1">
<div class="d-flex justify-content-center">
<a id="${capris[i].name}" class="btn btn-primary capri-btn" href="/capris.html" role="button">Buy ${capris[i].name}</a>
</div>
<p class="capri-description">${capris[i].description}</p>
</div>
`;
} else if (i >= 1) {
domString += `
<div class="carousel-item">
<img class="d-block w-100" src="${capris[i].imageUrl}" alt="Capri 1">
<div class="d-flex justify-content-center">
<a id="${capris[i].name}" class="btn btn-primary capri-btn" href="/capris.html" role="button">Buy ${capris[i].name}</a>
</div>
<p class="capri-description">${capris[i].description}</p>
</div>
`;
} else;
}
printToDom("#carousel-items", domString);
buyCapriBttn();
}
When the button is clicked, it runs the generateProduct function for that index. Here is a snippet of that function that is run:
const generateProduct = (selectedPants) => {
domString = '';
console.log(selectedPants);
for (let i = 0; i < capris.length; i++) {
if (capris[i] === selectedPants) {
domString += `
<div id="caprisDom">
<img id="buycaprispic" src="${capris[i].imageUrl}">
<div id="nameandrating">
<h5 id="buycaprisname">${capris[i].name}</h5><h2>☆☆☆☆☆</h2>
</div>
<div class="caprisinfobox">
<p id="caprisinfo">${capris[i].description}</p>
<div class="sizeandprice">
<div id="sizeselector">
<label id="sizelabel">Size:</label>
<select name="sizelist" id="size">
</select>
</div>
<h3 id="price">$${capris[i].price}</h3>
</div>
<button id="cartbutton">Add to Cart</button>
</div>
</div>
`;
}
}
printToDom('#caprismain', domString);
}
I have a printToDom function that prints to those ids if you need that as well.
NOTE: In this exercise, I am trying to not use JQuery
Any thoughts and help are much appreciated!
Is all you need the index position of the item? I'd probably add it dynamically in the a declaration. Something like <a id="${capris[i].name}" class="btn btn-primary capri-btn" href="/capris.html?index=${i}" role="button">Buy ${capris[i].name}</a>
So it should look like this:
const capriCarousel = () => {
let domString = '';
for (let i = 0; i < capris.length; i++) {
if (i === 0) {
domString += `
<div class="carousel-item active">
<img class="d-block w-100" src="${capris[i].imageUrl}" alt="Capri 1">
<div class="d-flex justify-content-center">
<a id="${capris[i].name}" class="btn btn-primary capri-btn" href="/capris.html?index=${i}" role="button">Buy ${capris[i].name}</a>
</div>
<p class="capri-description">${capris[i].description}</p>
</div>
`;
} else if (i >= 1) {
domString += `
<div class="carousel-item">
<img class="d-block w-100" src="${capris[i].imageUrl}" alt="Capri 1">
<div class="d-flex justify-content-center">
<a id="${capris[i].name}" class="btn btn-primary capri-btn" href="/capris.html?index=${i}" role="button">Buy ${capris[i].name}</a>
</div>
<p class="capri-description">${capris[i].description}</p>
</div>
`;
} else;
}
printToDom("#carousel-items", domString);
buyCapriBttn();
}
Then, in the other page, in your js, you'd grab the query select using URLSearchParams like so:
const urlParams = new URLSearchParams(window.location.search);
const myParam = urlParams.get('index');

Ajax / JQuery - Displaying flash message on success

I have a fully working flash system in PHP and am using it to send the user a success message once I create an entry in the DB.
On one of my forms I have a select field which I want the user to be able to seamlessly add entries too it without directing them away from a semi-completed form. The code I'm using is working well. The user clicks on 'add a category' (in the select label) it opens a modal, the user creates a new category, it updates the DB and the select field and closes the modal using AJAX. All working.
What I need to do is use or adapt my flash system to give the user a message to say all good your entry was added. I am very new to AJAX and on a steep learning curve!
This is my AJAX / JQUERY code: (I followed a tutorial to get here. The idea is to make this usable across the site when I need to add entries to a select, by adding 'ajax' to the form class.)
$('form.ajax').on('submit', function() {
var that = $(this),
url = that.attr('action'),
type = that.attr('method'),
data = {};
that.find('[name]').each(function(index,value) {
var that = $(this),
name = that.attr('name'),
value = that.val();
data[name] = value;
});
$.ajax({
url: url,
type: type,
data: data,
success: function(response) {
$('#select').load(document.URL + ' #select');
$('#addCategoryModal').modal('hide');
$('#siteMessage').toast('show');
}
});
return false;
});
And this is the PHP setting the DB record (working) and how I normally trigger a flash message on page reload (messages also work):
//create record in db
$newCategory = $this->blogModel->createCategory($formFields);
if ($newCategory) {
flash('siteMessage', 'Blog category added successfully');
} else {
flash('siteMessage', 'Something went wrong', 'bg-danger');
}
And this is the flash code:
function flash($name = '', $message = '', $class = 'bg-success') {
if (!empty($name)) {
if (!empty($message) && empty($_SESSION[$name])) {
if (!empty($_SESSION[$name])) {
unset($_SESSION[$name]);
}
if (!empty($_SESSION[$name.'_class'])) {
unset($_SESSION[$name.'_class']);
}
$_SESSION[$name] = $message;
$_SESSION[$name.'_class'] = $class;
} elseif (empty($message) && !empty($_SESSION[$name])) {
$class = !empty($_SESSION[$name.'_class']) ? $_SESSION[$name.'_class'] : '';
echo '
<div id="siteMessage" class="toast shadow" data-delay="8000" role="alert" aria-live="assertive" aria-atomic="true" style="position: absolute; top: 19px; right: 45%; z-index:10">
<div class="toast-header '.$class.'">
<i class="fas fa-envelope mr-2 pt-1 text-white"></i>
<strong class="mr-auto text-white">Site Message</strong>
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
<span class="text-white" aria-hidden="true">×</span>
</button>
</div>
<div class="toast-body">
'.$_SESSION[$name].'
</div>
</div>
';
unset($_SESSION[$name]);
unset($_SESSION[$name.'_class']);
}
}
}
My PHP processing page, creates the entry in the DB and I set the flash message as normal. I think I don't understand the interaction with how AJAX gets the returned success and setting a flash message.
Any thoughts?
Thanks to CBroe who pointed out the inherent problems with using a flash message mechanism I've added the following div at the bottom of the page and am now calling that direct with toast.show to give the message to the user.
I am not sure if that is the most affective way to do this but it works.
<div id="categoryMessage" class="toast shadow" data-delay="8000" role="alert" aria-live="assertive" aria-atomic="true" style="position: absolute; top: 19px; right: 45%; z-index:10">
<div class="toast-header bg-success">
<i class="fas fa-envelope mr-2 pt-1 text-white"></i>
<strong class="mr-auto text-white">Site Message</strong>
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
<span class="text-white" aria-hidden="true">×</span>
</button>
</div>
<div class="toast-body">
The category was added successfully
</div>
</div>

Progress Bar shows only in first time of consecutive file uploads in angular 6

In my Angular application, I have a separate form to upload profile picture. When I upload a new picture, confirmation modal will appear and after confirming, progress bar will visible in page and it will disappear after 100% completion. Without moving to another routing (still in same page) if I try to upload another picture, process will happen as same as previous except the progress bar. in second time progress bar will not visible. I need to show the progress bar in each file uploading. My codes are given bellow.
.html file
<div class="col-md-5 col-12 profile-content">
<form #imageForm enctype="multipart/form-data" novalidate>
<div class="img-content">
<div class="avatar-upload">
<div class="avatar-edit">
<input (change)="imagesChange($event.target.files)" type='file' id="fileName" name="fileName"
accept=".png, .jpg, .jpeg"/>
<label for="fileName"></label>
</div>
<div class="avatar-preview">
<div id="imagePreview" *ngIf="model.profilePic == null"
[style.background-image]="defaultBgImageVariable"></div>
<div *ngIf="model.profilePic" id="imagePreview"
[ngStyle]="{'background-image':'url(' + model.profilePic + ')'}"></div>
</div>
</div>
<div id="avatar" class="img-upload-progress" *ngIf="isAvatarUploading">
<div class="progress">
<div class="progress-bar" id="progressBar"></div>
</div>
<p id="uploadText">uploading...</p>
</div>
</div>
</form>
</div>
modal in same .html file
<div class="favorite-noti">
<div id="avatarUploadConfirm" class="modal fade" tabindex="-1" role="dialog"
aria-labelledby="favoriteNotification"
aria-hidden="true">
<div class="modal-dialog modal-confirm">
<div class="modal-content">
<div class="modal-body">
<h4>Do you want to upload profile picture ?.</h4>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-confirm" (click)="updateAvatar()">Upload</button>
<button type="button" class="btn btn-info" (click)="cancelModal()">Cancel</button>
</div>
</div>
</div>
.ts file
imagesChange(files) {
(<any>$('#avatarUploadConfirm')).modal('show');
this.myAvatar = files;
}
updateAvatar() {
(<any>$('#avatarUploadConfirm')).modal('hide');
this.handleFileInput(this.myAvatar);
}
cancelModal() {
(<any>$('#avatarUploadConfirm')).modal('hide');
}
handleFileInput(files) {
this.isAvatarUploading = true;
this.fileToUpload = files.item(0);
const formData = new FormData();
console.log('image-height', files.item.naturalHeight);
formData.append('fileName', this.fileToUpload, this.fileToUpload.name);
const user = this.authService.getUserFromLocalStorage();
if (user !== null) {
const url = `customers/profile-pic/${user.id}`;
this.restClientService.fileUpload(url, formData).subscribe(
response => {
console.log(response);
const imagePreview: HTMLElement = document.getElementById('imagePreview');
imagePreview.style.backgroundImage = 'url(\'' + response.message.profilePic + '\')';
this.model.profilePic = response.message.profilePic;
const elem = document.getElementById('progressBar');
const txt = document.getElementById('uploadText');
var width = 1;
const id = setInterval(frame, 10);
function timeoutToMissbar() {
document.getElementById('avatar').style.display = 'none';
}
function frame() {
if (width >= 100) {
clearInterval(id);
txt.innerHTML = 'Done 100%';
setTimeout(timeoutToMissbar, 2500);
} else {
width++;
elem.style.width = width + '%';
// elem.innerHTML = width * 1 + '%';
}
}
}
);
}
}
I need help to show the progress bar in every file upload.
You are setting display to none and not setting it to display again. Adding the below code at the start of handleFileInput should fix it.
setTimeout(() => {
document.getElementById('avatar').style.display = 'initial';
document.getElementById('uploadText').innerText = 'uploading...';
}, 0);
Better way - You can achieve the hiding and display part using the same variable isAvatarUploading instead of additionally using styles. You need to use Subject for that
public isAvatarUploading$ = new Subject();
handleFileInput() {
this.isAvatarUploading = true;
let timeoutToMissbar = () => {
// document.getElementById('avatar').style.display = 'none';
this.isAvatarUploading$.next(false);
}
And in html
*ngIf="isAvatarUploading$ | async"

Laravel Javascript - Check if theme exists in session, add class

I'm working on a system where the user can select a product and go to the next page. This product then gets saved in the session using laravel sessions. When the user decides to go to the next page and come back. The chosen product is indeed saved in the session, but the is no way for them to see what product they have chosen because the class isn't applied to the product that was chosen.
The code may clarify it better:
#foreach($themes as $theme)
<div class="col-md-4 mb-4">
<div class="card theme-card card-hover depth-2 border-0" id="theme-id-{{$theme->id}}">
<a href="" class="theme-link" data-toggle="modal" data-target="#theme{{ $theme->id }}">
<div class="card-image" style="height: 200px; background-image: url('/uploads/{{ $theme->thumbnail }}'); background-size: cover; background-position: center center;"></div>
<div class="card-body">
<div class="row">
<div class="col-md-2 vertical-center">
<i class="fab fa-magento fa-lg"></i>
</div>
<div class="col-md-10">
<p class="m-0">{!! str_limit($theme->name, $limit = 32, $end = '...') !!}</p>
<small class="text-muted">{{ $theme->productable_type }}</small>
</div>
</div>
</div>
</a>
<div class="card-footer bg-white border-0 text-right pt-0">
<div class="row">
<div class="col-md-6 text-left">
<input type="hidden" class="theme-name" name="theme[{{$theme->id}}]">
{{--<input type="hidden" value="{{ $theme->composer_package }}">--}}
<button data-card-id="{{$theme->id}}" class="btn btn-orange btn-sm btn-theme-choice">Kiezen</button>
</div>
<div class="col-md-6 text-right">
<span style="font-size: 20px;" >€ {{ $theme->price }} EUR</span>
</div>
</div>
</div>
</div>
</div>
#endforeach
In the above code, I for each trough every so-called "Theme" and I'm giving the ID of the theme as a data target and as ID. Then in my Javascript code, I do the following:
$('.btn-theme-choice').on('click', function (event) {
event.preventDefault();
newSelectedCardId = $(event.target).data('card-id');
if(cardId === null) {
cardId = newSelectedCardId;
} else if (cardId !== newSelectedCardId) {
$('#theme-id-' + cardId).removeClass('theme-chosen').find("input[name='theme["+cardId+"]']").val('');
cardId = null;
}
var card = $('#theme-id-' + cardId );
card.toggleClass('theme-chosen');
selectedCardInput = card.find("input[name='theme["+cardId+"]']");
if( !$('.theme-card').hasClass('theme-chosen') ) {
selectedCardInput.val('');
} else if ( $('.theme-card').hasClass('theme-chosen') ) {
selectedCardInput.val('selected');
}
console.log(selectedCardInput);
});
Here I add the class to the card so the user can See which card they've selected. This choice then gets saved in the session using some PHP code in the controller
if( $theme == null ) {
return redirect('plugins');
} elseif( $theme != null ) {
foreach($this->predefinedArray as $value) {
$request->session()->put('chosen_theme.' . $value, $theme->$value);
}
$request->session()->put('chosen_theme.composer_package', $theme->productable->composer_package);
return redirect('plugins');
}
problem
How can I read the session and add the class to the card with the IDs that were stored in the session so if the person leaves the page and comes back, they can see what cards they've selected?
Thanks in advance
Try this in your view..
<div class="card theme-card card-hover depth-2 border-0 {{ \Session::get('chosen_theme.composer_package') == $theme->id ? 'theme-chosen' : '' }}" id="theme-id-{{$theme->id}}">
Whenever the theme id is in the session and the page loaded the class will be added, and if it is not in the session then the class won't be added.
Let me know the result..

Categories