How to set player for course lesson in angular? - javascript

I have course details component. In this component I have 2 child component, for player and for lessons. Now when I click on one of the lesson it need to load in player the suitable video. Can any one help me with some ideas how can I do this ? How we can in Angular on click from one component send commands to other component ?
this is parent component
<div class='course-details'>
<div>
<app-course-player [course]='course'></app-course-player>
</div>
<div>
<app-lesson-card class='w-full' [course]='course'></app-lesson-card>
</div>
</div>
player component
<div *ngFor='let courseLesson of course.lessons'>
<vg-player class='w-full video-size'>
<vg-overlay-play vgFor='dreamclass-video'></vg-overlay-play>
<vg-controls>
<vg-play-pause></vg-play-pause>
<vg-playback-button></vg-playback-button>
<vg-scrub-bar>
<vg-scrub-bar-current-time></vg-scrub-bar-current-time>
<vg-scrub-bar-buffering-time></vg-scrub-bar-buffering-time>
</vg-scrub-bar>
<vg-time-display vgProperty='left' vgFormat='mm:ss'></vg-time-display>
<vg-mute></vg-mute>
<vg-fullscreen></vg-fullscreen>
</vg-controls>
<video #myMedia
[vgMedia]='myMedia'
[poster]='"img-proxy/plain/" + courseLesson.coverUrl'
id='dreamclass-video'
[src]='courseLesson.videoUrl'>
</video>
</vg-player>
</div>
get data from here
#Input() course: ICourse;
lesson component
<div class='course-section w-full lg:w-auto border border-white-gray mt-4 lg:mt-0'>
<div class='flex items-center justify-between'>
<p class='text-primary text-2xl price-box font-medium'>Free</p>
</div>
<div class='theme-section border-t border-b border-white-gray'>
<div class='flex items-center justify-between top-margin' *ngFor='let lessonData of course.lessons'>
<div class='flex items-center'>
<img src='assets/images/sliders/lock.svg' class='mr-right' alt=''>
<p class='text-sm font-normal text-darkgray course-box-title'>{{lessonData.title}}</p>
</div>
<div><p
class='text-sm font-normal text-regulargray'>{{lessonData.duration * 1000 | date: 'mm:ss'}}</p>
</div>
</div>
</div>
<div>
<button *ngIf='!course?.enrolled' mat-raised-button class='enrol-butn' color='primary'
(click)='enrollCourse()'> Enroll
</button>
<button *ngIf='course?.enrolled' mat-raised-button class='enrol-butn text-white' color='accent'
(click)='enrollCourse()'> Enrolled
</button>
</div>
</div>

Another way is using a service.
Create a service:
ng g s MyServiceName --skip-Tests
Once it is created, check that has been decorated as providedIn root:
#Injectable({
providedIn: 'root',
})
export class MyServiceNameService {
....
In your service, declare all you want the components have in commun.
e.g:
private _courseSelected: ICourse
Inject the service in the constructor of your components (in course-player and in lesson-card):
...
import { MyServiceNameService } from '../../services/my-service-name.service';
...
constructor(
private myServiceNameService : MyServiceNameService ) {
}
...
Used directly the variables in the service from your components, or better, make methods in the service to modify the varaibles values.
e.g:
in service:
...
export class MyServiceNameService {
public get courseSelected() {
return this._courseSelected;
}
constructor(){}
public someFunctionThaCausesTheVideoSelectionChanges(newVideoUrl: string){
this._courseSelected = newVideoUrl;
}
...
in component:
const myCourseSelectede = this.myServiceNameService.courseSelected;
....
setNewCourse ( newCourse: string) {
this.myServiceNameService.someFunctionThaCausesTheVideoSelectionChanges(newCourse);
}

You can solve this using an Output event in the lesson-card component and using template ids in the parent component.
Something like:
<div class='course-details'>
<div>
<app-course-player #player [course]='course'></app-course-player>
</div>
<div>
<app-lesson-card class='w-full' (videoSelect)="player.play($event)" [course]='course'></app-lesson-card>
</div>
</div>
In the lesson-card component declare the Output Event:
export class LessonCardComponent {
#Output()videoSelect = new EventEmitter<string>('');
...
someFunctionThaCausesTheVideoSelectionChanges(){
videoSelect.emit('new video url');
}
}
In the course-player component, declare the play function
export class CoursePlayerComponent{
...
play(videoUrl: string){
...
}
}

Related

React / NextJS: limit the rendering of a component and update props instead

I have a NextJS application that displays a list of audio tracks through a component called <AudioTrackEntry>. This component receives props such as the Track Title and a Link to the audio file through a mapping from an external data source.
Nested within the <AudioTrackEntry> component is the <AudioPlayer> that is toggled through a play button.
This currently results in multiple (overlapping) audio players being rendered, when play buttons on multiple audio track entries are being clicked. Clicking 3 Play buttons on 3 tracks result in 3 audio tracks simultaneously playing.
The behaviour I had intended was to just update the respective props of trackTitle and trackLink.
I assume that the reason why the player is being re-rendered is that I'm toggling the visibility state of the AudioPlayer component, and when the state changes, the component gets re-rendered.
How can I limit the button toggle to only update the props of trackTitle and trackLink, not render another player here?
import React, { useEffect, useState } from 'react';
import AudioPlayer from './AudioPlayer';
interface AudioTrackEntryProps {
trackLink: any;
trackClaimStatus: any;
trackTitle: any;
trackDuration: any;
trackBPM: any;
trackGenre: any;
trackClaimAmount: any;
}
const AudioTrackEntry: React.FC<AudioTrackEntryProps> = (props) => {
const [activeTrack, setActiveTrack] = useState('');
const toggleAudioPlayer = (trackTitle: string) => {
setActiveTrack(activeTrack === '' ? trackTitle : '');
};
return (
<div>
<div className="grid grid-cols-12 gap-4 border-b border-cllinegray p-2">
<div className="col-span-1 flex items-center">
<button
onClick={() => {
toggleAudioPlayer(props.trackTitle);
}}
className="border-transparent bg-transparent hover:border-transparent"
>
Play </button>
</div>
<div className="col-span-3 flex items-center">
<h4 className="m-0 font-bold uppercase">{props.trackTitle}</h4>
</div>
<div className="col-span-1 m-auto text-neutral-300">
<p>{props.trackDuration}</p>
</div>
<div className="col-span-1 m-auto text-neutral-300">
<p>{props.trackBPM}</p>
</div>
<div className="col-span-1 m-auto text-neutral-300">
<p>{props.trackGenre}</p>
</div>
<div className="col-span-1 m-auto text-xs text-neutral-300">
<InstrumentalButton />
</div>
<div className="col-span-1 m-auto text-xs text-neutral-300">
<RemixButton />
</div>
{activeTrack === props.trackTitle && (
<AudioPlayer
trackLink={props.trackLink}
trackTitle={activeTrack}
trackDuration={props.trackDuration}
/>
)}
</div>
);
};
export default AudioTrackEntry;
Here is the code of the <AudioPlayer> component:
import {
MediaControlBar,
MediaController,
MediaMuteButton,
MediaPlayButton,
MediaTimeDisplay,
MediaTimeRange,
MediaVolumeRange,
} from 'media-chrome/dist/react';
import Image from 'next/image';
import React from 'react';
import TrackIcon from '#/public/assets/images/icons/track_icon.svg';
function AudioPlayer(props: {
trackLink: any;
trackTitle: string;
trackDuration: string | number;
}) {
return (
<div className="fixed inset-x-0 bottom-0 z-30 rounded-md bg-neutral-900 p-6">
<div className="flex">
<div className="w-1/12">
<Image src={TrackIcon} alt="Full Circus" />
</div>
<div className="flex w-3/12 items-center">
<h4 className="font-bold uppercase">{props.trackTitle} </h4>
</div>
<div className="mx-auto flex w-8/12 items-center">
<MediaController audio className="w-full bg-neutral-900">
<audio src={props.trackLink} autoPlay slot="media"></audio>
<MediaControlBar className="bg-neutral-900">
<MediaPlayButton className="bg-transparent"></MediaPlayButton>
<MediaTimeRange className="w-80 bg-transparent"></MediaTimeRange>
<MediaTimeDisplay
className="w-32 bg-transparent font-trade"
showDuration
></MediaTimeDisplay>
<div className="mx-24 flex">
<MediaMuteButton className="bg-transparent"></MediaMuteButton>
<MediaVolumeRange className="w-80 bg-transparent"></MediaVolumeRange>
</div>
</MediaControlBar>
</MediaController>
</div>
</div>
</div>
);
}
export default AudioPlayer;
You'll want to move the state that controls the "active track" to a component higher up the component tree than all your AudioPlayer instances, and pass the appropriate state-setter down. For instance,
interface AudioTrackDetails {
trackLink: any;
trackClaimStatus: any;
... etc
}
const AudioTrackList: FC<{ availableTracks: AudioTrackDetails[] }> =
({ availableTracks }) =>
{
const [activeTrackTitle, setActiveTrackTitle] = useState<string | null>(null);
return <>{ availableTracks.map(entry =>
<AudioTrackEntry {...entry} key={activeTrack.trackTitle}
activeTrackTitle={activeTrackTitle}
setActiveTrack={setActiveTrackTitle}
/>
}</>
}
interface AudioTrackEntryProps extends AudioTrackDetails {
activeTrackTitle: string | null;
setActiveTrackTitle: (newTrack: (oldTrack: string | null) => string | null) => void;
}
const AudioTrackEntry: React.FC<AudioTrackEntryProps> = (props) =>
{
const toggleAudioPlayer = (trackTitle: string) => {
setActiveTrackTitle(
oldTitle => oldTitle !== trackTitle ? trackTitle : ""
);
};
...
}
This essentially gives you a mutex controlled by this parent AudioTrackList which ensures that only one (or zero) of the AudioTrackEntry instances can exist at a time. If the user toggles an inactive track then it hides the currently-active track; if the user toggles the active track then it stops playing altogether.

Vue accessing a child components computed property

So I'm using a third party vue component called 'vue-tree-list' here's the link -> https://github.com/ParadeTo/vue-tree-list
in the component it has a computed property that basically analyzes the tree structure to find the right place for a new leaf/node to be inserted.
In my parent component I did this:
<template>
<div class="py-8 px-5" style="min-height: calc(100vh - (112px + 2.75rem))">
<div class="flex flex-col w-full">
<button class="cursor-pointer relative flex flex-row items-center h-10 focus:outline-none "
>
<span class="text-sm tracking-wide truncate ml-6">Add Node</span>
</button>
</div>
<VueTreeList
#click="onClick"
#change-name="onChangeName"
#delete-node="onDel"
#add-node="onClick"
ref="tree"
:model="data"
default-tree-node-name="New Depot"
default-leaf-node-name="New Driver"
v-bind:default-expanded="false"
>
<template v-slot:leafNameDisplay="slotProps">
<a class="text-orange-primary mr-4">
<span>{{ slotProps.model.name }}</span>
</a>
</template>
<span class="icon" slot="addTreeNodeIcon">📂</span>
<span class="icon" #click.stop="test()" slot="addLeafNodeIcon">+</span>
^INSTEAD OF CALLING THE DEFAULT EVENT 'add-child' WHICH IMMEDIATELY INSERTS A NODE I DIVERTED IT INSTEAD SINCE I WANT THE USER TO INPUT THEIR DATA BEFORE INSERTING INSIDE THE TREE
<span class="icon" slot="editNodeIcon">📃</span>
<span class="icon" slot="delNodeIcon">✂️</span>
<span class="icon" slot="leafNodeIcon">🍃</span>
<span class="icon" slot="treeNodeIcon">📂</span>
</VueTreeList>
<Modal ref="modal" :title="modalTitle" :size="modalSize" :height="modalHeight">
<div v-if="modalContent == 'new'">
<DriverLookUp />
<VehicleLookUp />
</div>
</Modal>
</div>
</template>
<script>
import { VueTreeList, Tree, TreeNode } from 'vue-tree-list'
import { DriverLookUp, VehicleLookUp } from '#/components/forms/depot'
export default { ONLY ADDED THE RELEVANT FUNCTIONS SINCE IT WOULD BE VERY LONG
components: {
VueTreeList, Modal, DriverLookUp, VehicleLookUp
},
test(){
this.$refs.tree.rootNode() <--- the computed method that I want to access
},
}...
The problem with this is that the computed property for some reason throws an error on missing properties which doesn't make sense since it has already been rendered. Is there a way to trigger a child components computed property?
https://github.com/ParadeTo/vue-tree-list/blob/master/src/VueTreeList.vue <--- here's the link of the child component that I'm working with
That component's computed prop walks up its parent tree until it finds a component with a prop named model, containing name of "root". It assumes all parents have this prop, and fails otherwise, leading to the error you observed.
A workaround is to declare that property in your component before reading the computed prop:
export default {
props: {
👇
model: {
type: Object,
default: () => ({ name: 'root' }),
},
},
methods: {
test() {
const rootNode = this.$refs.tree.rootNode
console.log({ rootNode })
},
}
}
demo

Method "getChampionName" has type "undefined" in the component definition. Did you reference the function correctly?

I'm trying to use this function (src/api/)
function getChampionName(champId) {
axios.get('http://ddragon.leagueoflegends.com/cdn/12.5.1/data/en_US/champion.json')
.then(({ data }) => {
let list = data
let championList = list.data
for (var i in championList) {
if (championList[i].key == champId) {
return championList[i].id
}
}
})
.catch((err) => console.log(err))
}
export {getChampionName}
In this component (src/components/)
<template>
<div class="w-72">
<header class="rounded-tl-lg rounded-tr-lg bg-slate-400 p-0.5">
<p>Champion's Mastery</p>
</header>
<ul class="grid gap-1 rounded-bl-lg rounded-br-lg bg-slate-50 p-0.5">
<li v-for="champ in masteryData.slice(0,10)" :key="champ.championId">
<div class="flex items-center justify-between">
<div class="flex items-center gap-2">
<img src="https://ddragon.leagueoflegends.com/cdn/12.5.1/img/champion/Karma.png" alt="" class="rounded-lg h-14 w-14">
<div>
<p class="font-medium text-center">{{ getChampionName(champ.championId) }}</p>
<p>Level {{ champ.championLevel }}</p>
</div>
</div>
<p class="text-2xl font-medium">{{ champ.championPoints }} Pts</p>
</div>
</li>
</ul>
</div>
</template>
<script>
import getChampionName from '#/api/search'
export default{
name: 'MasteryInfo',
props: [
'masteryData'
],
methods: {
getChampionName
}
}
</script>
But I'm getting this error Method "getChampionName" has type "undefined" in the component definition. and don't know what does it mean.
It seems you didn't import the method properly.
Change the import into:
import { getChampionName } from '#/api/search';
You can read more about import export in javascript here:
https://javascript.info/import-export
If you think you almost have the same question as this question, you can also refer to this:
Method "showDate" has type "undefined" in the component definition

I am new to react. I want to render a child component using a single state using an onClick event in react JS

on Click of button the state should be able to render component using statename.map.. Thankyou
<div className="container mt-5">
<div className="row">
<div className="card pt-3">
<div className="col-lg-12">
<h4>Promotional Rates</h4>
<p>
Create promotional rate(s)
</p>
<button className="btn btn-primary my-3" onClick={???}>
Add New Promotional Rates
</button>
<<<<<<<render child component here using .map>>>>>>>>>
</div>
</div>
</div>
</div>
creat a state and u can use whatever method in the js code bellow
import React,{useState} from "react"
const ParentComponent = () =>{
const [ShowChild,setShowChild]=useState(false)
return(
<div>
//methode 1
{ShowChild && ChildComponent}
// end methode 1
//methode 2
{ShowChild? <ChildComponent /> : ''}
//end methode 2
<button onClick={()=>setShowChild(!ShowChild)}>show child Button </button>
</div>
)}
const ChildComponent = () => {
return(
<h1>I m a child</h1>
)
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

Reactjs How to pass the props of current page into child page

This is Clients_info.js
In this component's props, it has several values.
And now I want to pass all the props in this component to the Modalbox component.
I know how to pass value from the current state to child component in render function as props. But....from props to props...
How could I make that? Thanks!
import React from "react";
import Modalbox from './Client_modal'
require('../../css/Clients.scss');
var $ = require ('jquery');
export default class Clients_info extends React.Component {
constructor(props) {
super(props);
}
//Invoked once, both on the client and server, immediately before the initial rendering occurs.
componentWillMount(){
}
render() {
return(
<div id='tabbox-order' className='clients_info'>
<div id='clientsInfo_wrapper'>
<div id='clientsInfo_row'>
<div id='ava_wrapper'>
<img id='clietnsInfo_avatar'></img>
<p>{this.props.client.name}</p>
</div>
<div id='infor_wrapper'>
<p><i class="material-icons">email</i> Email: {this.props.client.email}</p>
<p><i class="material-icons">phone</i> Phone: {this.props.client.phone}</p>
<p><i class="material-icons">location_on</i> Address: {this.props.client.loc}</p>
<p><i class="material-icons">my_location</i> Zip Code: {this.props.client.zip}</p>
</div>
</div>
<div id='key' >
<i class="material-icons">vpn_key</i>{this.props.client.key}
</div>
<div id='Cutting' ></div>
<div>
<h4>Pets Information</h4>
{ this.props.pets.map(function(pet) {
return(
<div>
<div className='row'key={pet.id}>
<div className='col-md-3' >avatar</div>
<div className='col-md-3' >{pet.petName}</div>
<div className='col-md-3' >{pet.breed}</div>
<div className='col-md-3' >{pet.age}</div>
</div>
<div id='pet-detail'>
<p>Extra Information:</p>
<input placeholder='This dog is crazy!!!'>
</input>
</div>
</div>
)
})
}
</div>
<div id='Cutting' ></div>
<Modalbox/>
</div>
</div>
);
}
}
<Modalbox pets={ this.props.pets }/>
Should do the job

Categories

Resources