Vue.js and django rest framework for implements a cascading dropdown list - django

I would need help building a request to my backend API.
I currently have a form with a drop down list. The data in this list comes from this.$Http.get('/ quality/api/affaires/')
Below this drop-down list, I have another list. This list, I would like it to be empty until the 1st is not selected, then it is implemented with data according to the selection above.
Backend side (Django) I have 2 models which are "Affaires" and "AffairesOfs". I used Serialize and I can therefore request each of these models via api/affaires and api/affairesofs
In the "AffairesOfs" model I have a foreignekey (idaffaire) on the id of the "Affaires" model.
Finally, I would like my second list to be made up of all the “affairesofs” linked to the “Affaires” selected.
For now, I have my 2 drop-down lists but I can't find a way to link the second to the first.
I tried different methods found on the internet (with the use of v-model, ...) but could not achieve a result.
I can't even get the value selected from the first list to display it in the console, or in a <span>. I think I need a change event on the first list which ask a getMethod with selected value in parameters ?
example of api/affaire :
{
"id": 1,
"nom": "HORS AFFAIRE",
"adresse": "15, rue de la Gibaudière",
"cp": "49183",
"ville": "Saint-Barthélémy d'Anjou",
"dessinateur": 0,
"conducteur": 0,
"chefdeprojet": null,
"cloture": 0
},
{
"id": 2,
"nom": "Suivi Production",
"adresse": null,
"cp": null,
"ville": null,
"dessinateur": null,
"conducteur": null,
"chefdeprojet": null,
"cloture": 0
},
example of api/affairesofs :
{
"id": 2,
"idaffaire": {
"id": 1042,
"nom": "Schlumberger",
"adresse": "",
"cp": "75007",
"ville": "Paris",
"dessinateur": null,
"conducteur": 6,
"chefdeprojet": 16,
"cloture": 1
},
"dateajout": "2015-12-14T15:08:46Z",
"statut": 2,
"type": 0,
"nom": "Chassis St Do R1 à R3",
"isanalise": 1,
"idpersonnel": 1
},
{
"id": 6,
"idaffaire": {
"id": 1045,
"nom": "LAVAL",
"adresse": "",
"cp": "53000",
"ville": "Laval",
"dessinateur": 3,
"conducteur": 9,
"chefdeprojet": 9,
"cloture": 1
},
and below there is my page :
<div id="starting">
<div class="container">
<div class="row">
<form class="form-group">
<label>N° d'affaire</label>
<select class="col" v-model="affaireSelected">
<option value="">Choisir :</option>
<option v-for="affaire in affaires" v-bind:value="affaire.id">${affaire.id} - ${affaire.nom}</option>
</select>
<span> Selectionné : {{ affaireSelected }}</span>
<label>N° d'OF</label>
<select class="col">
<option value="choisir">Choisir :</option>
<option v-for="of in ofs" :value="of.id">${of.id} - ${of.nom}</option>
</select>
<input type="submit" value="Valider" class="btn btn-success" />
</form>
</div>
</div>
<div class="loading" v-if="loading===true">Loading…/div>
</div>
<!-- vue.js files !-->
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.13/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue-resource#1.3.5"></script>
<script type="text/javascript">
new Vue({
el: '#starting',
delimiters: ['${','}'],
data: {
ncs: [],
affaires: [],
ofs: [],
affaireSelected: '',
loading: false,
currentNc: {},
},
mounted: function() {
this.getAffaires();
this.getOfs();
},
methods: {
getAffaires: function() {
this.loading = true;
this.$http.get('/qualite/api/affaires/')
.then((response) => {
this.affaires =response.data;
this.loading = false;
})
.catch((err) => {
this.loading = false;
console.log(err);
})
},
getOfs: function() {
this.loading = true;
this.$http.get('/qualite/api/affairesOf/')
.then((response) => {
this.ofs =response.data;
this.loading = false;
})
.catch((err) => {
this.loading = false;
console.log(err);
})
},
}
});
</script>

Finally found a solution. I need to use a computed property juste like this :
computed: {
ofsByAffaire() {
return this.ofs.filter(oF => oF.idaffaire.id === this.affaireSelected.id);
}
},
then, I juste have to use this computed property on the template :
<select class="col">
<option value="choisir">Choisir :</option>
<option v-for="of in ofsByAffaire" :value="of.id">${of.id} - ${of.nom}</option>
</select>

Related

vue3 filtered data on computed with chartjs

im learning about vuejs 3 and for example i want to show a chartjs with a combo to filter data. (Without the combo, everything works correctly)
i have a parent component chartjs.vue where import the chartjs and the combo. when i select a year, i change the variable and show it on the template
<script setup>
import ChartJsBarChart from '#/views/charts/chartjs/ChartJsBarChart.vue'
import SelectYear from '#/componentes/selectYear.vue'
const api = ref('exp')
const year = ref([])
function selectYear(x) {
year.value = x
}
</script>
<template>
<!-- 👉 Latest Statistics -->
<VCol cols="24" md="12">
<VCard>
<VCardItem class="d-flex flex-wrap justify-space-between gap-4">
<VCardTitle>Choose a year</VCardTitle>
</VCardItem>
<VCardText>
<SelectAno #input="selectAYear" />
</VCardText>
</VCard>
</VCol>
<VCol cols="24" md="12">
<VCard>
<VCardItem class="d-flex flex-wrap justify-space-between gap-4">
<VCardTitle>Total</VCardTitle>
</VCardItem>
<VCardText>
<ChartJsBarChart :yearSelected="year" :url="api" />
</VCardText>
</VCard>
</VCol>
</template>
one child component with a select (to try props and emit) selectYear.vue
<script setup>
import { ref, defineEmits, watch } from 'vue'
const emit = defineEmits(['input'])
let selected = ref([])
const years = [
'2005',
'2008',
'2009',
'2010',
'2011',
'2012',
'2014',
'2015',
'2016',
'2017',
'2018',
'2019',
'2020',
'2021',
'2022',
'2023',
]
watch(
() => selected.value,
(newValue, oldValue) => {
change(newValue)
},
)
const change = val => emit('input', val)
</script>
<template>
<VSelect
v-model="selected"
:items="years"
label="choose a year"
/>
</template>
and last child component with the chartjs ChartJsBarChart.vue
<script setup>
import BarChart from '#/libs/chartjs/components/BarChart'
import axios from '#axios'
import { ref, onMounted, computed } from "vue"
import { useStatesStore } from '#/store/states'
const props = defineProps({
url: {
type: null,
required: true,
},
yearSelected: {
type: null,
required: true,
},
})
const apiData = ref([]) //data from API
const data = ref({}) // data parsed to chartjs
const store = useStatesStore()
const states = ref(store.states) // legend
const filterByYear = computed(() => {
return apiData.value.filter(item => item.year === '2005')
})
onMounted(async () => {
await axios
.get('/api/dashboard/expProvActiv')
.then(res => {
apiData.value = res.data['hydra:member']
})
})
</script>
<template>
{{ props.yearSelected }}
<hr>
{{ apiData }}
<BarChart
:height="400"
:chart-data="data"
/>
</template>
the result (shortened) of {{ apiData }} with hundred of items is:
[ { "state": "state 1", "activity": "string", "subactivity": "string", "year": 2015, "surface": "3.5500", "total": 3, "rcs": 3 }, { "state": "state 2", "activity": "string", "subactivity": "string", "year": 2016, "surface": "10.9400", "total": 13, "rcs": 13 }]
if i try to show filterByYear, the first time i have not data and how can i do to call filterByYear when i change the year?
[answer myself] the year was string instead number
how can i update the child component BarChart to update the information?
thank you!

braintree hosted payment fields client undefined Ember 3.25

Ember and Braintree Hosted Fields are not a good mix so far, Braintree Support are out of ideas on this one. When the form renders on the page it calls the action to create the client. The client is undefined.
picture-this-44ac48bef9f8df633632a4d202da2379.js:57 Uncaught TypeError: Cannot read property 'client' of undefined
component hbs
<script src="https://js.braintreegateway.com/web/3.81.0/js/client.min.js"></script>
<script src="https://js.braintreegateway.com/web/3.81.0/js/hosted-fields.min.js"></script>
<div class="demo-frame" {{did-insert this.setupBraintreeHostedFields}}>
<form action="/" method="post" id="cardForm" >
<label class="hosted-fields--label" for="card-number">Card Number</label>
<div id="card-number" class="hosted-field"></div>
<label class="hosted-fields--label" for="expiration-date">Expiration Date</label>
<div id="expiration-date" class="hosted-field"></div>
<label class="hosted-fields--label" for="cvv">CVV</label>
<div id="cvv" class="hosted-field"></div>
<label class="hosted-fields--label" for="postal-code">Postal Code</label>
<div id="postal-code" class="hosted-field"></div>
<div class="button-container">
<input type="submit" class="button button--small button--green" value="Purchase" id="submit"/>
</div>
</form>
</div>
component class
import Component from '#glimmer/component';
import { action } from '#ember/object';
import { inject as service } from '#ember/service';
import { tracked } from '#glimmer/tracking';
import { braintree } from 'braintree-web';
export default class CardPaymentComponent extends Component {
#action
setupBraintreeHostedFields() {
alert('booh');
var form = document.querySelector('#cardForm');
var authorization = 'sandbox_24nzd6x7_gyvpsk2myght4c2p';
braintree.client.create({
authorization: authorization
}, function(err, clientInstance) {
if (err) {
console.error(err);
return;
}
createHostedFields(clientInstance);
});
function createHostedFields(clientInstance) {
braintree.hostedFields.create({
client: clientInstance,
styles: {
'input': {
'font-size': '16px',
'font-family': 'courier, monospace',
'font-weight': 'lighter',
'color': '#ccc'
},
':focus': {
'color': 'black'
},
'.valid': {
'color': '#8bdda8'
}
},
fields: {
number: {
selector: '#card-number',
placeholder: '4111 1111 1111 1111'
},
cvv: {
selector: '#cvv',
placeholder: '123'
},
expirationDate: {
selector: '#expiration-date',
placeholder: 'MM/YYYY'
},
postalCode: {
selector: '#postal-code',
placeholder: '11111'
}
}
}, function (err, hostedFieldsInstance) {
var tokenize = function (event) {
event.preventDefault();
hostedFieldsInstance.tokenize(function (err, payload) {
if (err) {
alert('Something went wrong. Check your card details and try again.');
return;
}
alert('Submit your nonce (' + payload.nonce + ') to your server here!');
});
};
form.addEventListener('submit', tokenize, false);
});
}
}
}
package.json
...
"ember-cli": "^3.25.2",
"braintree-web": "^3.81.0",
...
** Final Solution **
NPM braintree-web not required. Component class does not have access to the Braintree Window object. Move the tags to the app/index.html as outlined in the accepted answer.
component hbs
<article class="rental">
<form action="/" method="post" id="cardForm">
<label class="hosted-fields--label" for="card-number">Cardholder Name</label>
<div id="card-holder-name" class="hosted-field payment"></div>
<label class="hosted-fields--label" for="card-number">Email</label>
<div id="email" class="hosted-field payment"></div>
<label class="hosted-fields--label" for="card-number">Card Number</label>
<div id="card-number" class="hosted-field payment"></div>
<label class="hosted-fields--label" for="expiration-date">Expiration Date</label>
<div id="expiration-date" class="hosted-field payment"></div>
<label class="hosted-fields--label" for="cvv">CVV</label>
<div id="cvv" class="hosted-field payment"></div>
<label class="hosted-fields--label" for="postal-code">Postal Code</label>
<div id="postal-code" class="hosted-field payment"></div>
<div class="button-container">
<input type="submit" class="button" value="Purchase" id="submit"/>
</div>
</form>
</article>
<script>
var form = document.querySelector('#cardForm');
var authorization = 'sandbox_24nzd6x7_gyvpsk2myght4c2p';
braintree.client.create({
authorization: authorization
}, function(err, clientInstance) {
if (err) {
console.error(err);
return;
}
createHostedFields(clientInstance);
});
function createHostedFields(clientInstance) {
braintree.hostedFields.create({
client: clientInstance,
styles: {
'input': {
'font-size': '1.2em',
'font-family': 'courier, monospace',
'font-weight': 'lighter',
'color': '#ccc'
},
':focus': {
'color': 'black'
},
'.valid': {
'color': '#8bdda8'
}
},
fields: {
number: {
selector: '#card-number',
placeholder: '4111 1111 1111 1111'
},
cvv: {
selector: '#cvv',
placeholder: '123'
},
expirationDate: {
selector: '#expiration-date',
placeholder: 'MM/YYYY'
},
postalCode: {
selector: '#postal-code',
placeholder: '11111'
}
}
}, function (err, hostedFieldsInstance) {
var tokenize = function (event) {
event.preventDefault();
hostedFieldsInstance.tokenize(function (err, payload) {
if (err) {
alert('Something went wrong. Check your card details and try again.');
return;
}
alert('Submit your nonce (' + payload.nonce + ') to your server here!');
});
};
form.addEventListener('submit', tokenize, false);
});
}
</script>
You can use Braintree SDK via either the direct script tag or using the npm module with the help of ember-auto-import. In your case, you are using both.
For simplicity, let's use the script tag to inject the SDK. The issue in your snippet is that you are trying to load the script tag inside a component handlebar file. the handlebars (.hbs file) cannot load scripts using a <script> tag. We need to move the script tag to the index.html file present inside the app folder. This will load the SDK properly to be used inside a component.
app/index.html:
<body>
...
<script src="https://js.braintreegateway.com/web/3.81.0/js/client.min.js"></script>
<script src="https://js.braintreegateway.com/web/3.81.0/js/hosted-fields.min.js"></script>
{{content-for "body-footer"}}
</body>
Once you inject the SDK properly, you can use the braintree window object without any issue.

Vue.js instant search from API REST Framework using axios

I have a problem. I want to create instant search, without any search button, that when i'm typing e.g. more than 3 letters, my results will be instant show below.
My code:
<template>
<div class="nav-scroller py-1 mb-2">
<div class="nav d-flex justify-content-between">
<input v-model="keyword" class="form-control" type="text" placeholder="Search" aria-label="Search">
<div v-bind:key="result.id" v-for="result in results">
<p>Results are: {{ result.title }}</p>
</div>
</div>
</div>
</template>
<script>
import axios from 'axios';
export default {
name: 'Home',
components: {
},
data() {
return {
keyword: '',
results: [],
}
},
methods: {
getResults() {
axios.get("http://127.0.0.1:8000/api/v1/books/?search="+this.keyword)
.then(res => (this.results = res.data))
.catch(err => console.log(err));
}
},
created() {
this.getResults()
}
}
</script>
Now my 'keyword' parameter is probably not passed to the url, because when I refresh the page, all records from APi are the results.
Could you help me?
You should either call method when input changes
<input v-model="keyword" #input="getResults">
and method:
getResults() {
if (this.keyword.length > 3)
axios.get("http://127.0.0.1:8000/api/v1/books/?search="+this.keyword)
.then(res => (this.results = res.data))
.catch(err => console.log(err));
}
}
Or watcher can be used. When keyword changes watcher will call getResults method.
watch: {
keyword: "getResults"
}
Use watcher for the keyword value update.
Whenever keyword is more than 3 letters, request the getResults() method to search.
export default {
name: 'Home',
components: {
},
data() {
return {
keyword: '',
results: [],
}
},
watch: {
keyword: function(newVal) {
if (newVal.length >2) {
this.getResults();
}
}
},
methods: {
getResults() {
axios.get("http://127.0.0.1:8000/api/v1/books/?search="+this.keyword)
.then(res => (this.results = res.data))
.catch(err => console.log(err));
}
},
created() {
this.getResults()
}
}

ionic 2 - Using Highchart in the Segment

I use HighChart library in the Segment, my segment have 2 tab DASHBOARD and NEW, my Chart in the DASHBOARD tab. First run: My Chart run, but i click to New tab and come back DASHBOARD tab => My chart not run ?
[sorry, i'm not good english]
-- My code html:
<div class="segment-chart">
<ion-segment [(ngModel)]="pet">
<ion-segment-button value="dashboard" (ionSelect)="selectedFriends()">
DASHBOARD
</ion-segment-button>
<ion-segment-button value="new">
NEW
</ion-segment-button>
</ion-segment>
</div>
<div [ngSwitch]="pet">
<div class="chart" *ngSwitchCase="'dashboard'">
<!--View Chart-->
<div #chart>
<chart type="StockChart" [options]="options"></chart>
</div>
</div>
<ul *ngSwitchCase="'new'" style="list-style-type:none" class="div-new-body">
<li class="div-new-li" *ngFor="let new of lsNews">
<div class="div-new-detail">
<div class="div-new-title">
{{new.date}}
</div>
<div class="div-new-content">
{{new.title}}
</div>
</div>
<div class="div-new-nav">></div>
</li>
</ul>
</div>
My code file ts:
export class ChartPage implements AfterViewInit, OnDestroy {
private _chart: any;
lastData: any
lstData: any = []
pet : any
lsNews : any = []
opts : any;
#ViewChild('chart') public chartEl: ElementRef;
//chartOption: any
// Destroy Chart
ngOnDestroy(): void {
// throw new Error("Method not implemented.");
console.log("OnDestroy run")
var chart = this._chart
chart.destroy();
}
// option Chart
ngAfterViewInit() {
if (this.chartEl && this.chartEl.nativeElement) {
this.opts.chart = {
// type: 'area',
renderTo: this.chartEl.nativeElement,
backgroundColor: {
linearGradient: [0, 0, 500, 500],
stops: [
[0, '#3d4d64'],
[1, '#3d4d64']
]
},
height: '90%',
spacingBottom: 15,
spacingTop: 10,
spacingLeft: 10,
spacingRight: 10,
};
console.log('chart create ss')
this._chart = new Highcharts.StockChart(this.opts);
}
}
constructor(public navCtrl: NavController, public navParams: NavParams, public service: Service) {
const me = this;
this.pet= 'dashboard';
setInterval(function () {
if (me._chart) {
me._chart['series'][0].addPoint([
(new Date()).getTime(), // gia tri truc x
//5// gia tri truc y
me.getData()
],
true,
true);
}
}, 3000);
this.opts = {
credits: {
enabled: false
},
xAxis: {
type: 'datetime',
tickPixelInterval: 150,
labels: {
style: {
color: '#705f43' // color time
}
},
lineColor: '#705f43' // 2 line cuoi mau trang
},
yAxis: {
gridLineColor: '#705f43', //line gach ngang
labels: {
style: {
color: '#fff'
}
},
lineColor: '#ff0000',
minorGridLineColor: '#ff0000',
tickColor: '#fff',
tickWidth: 1,
title: {
style: {
color: '#ff0000'
}
}
},
navigator: {
enabled: false
},
rangeSelector: {
buttons: [{
count: 1,
type: 'minute',
text: '1M'
}, {
count: 5,
type: 'minute',
text: '5M'
}, {
type: 'all',
text: 'All'
}],
inputEnabled: false,
selected: 0,
},
series: [{
name: 'Random data',
data: (function () {
// generate an array of random data
var data = [],
time = (new Date()).getTime(),
i;
for (i = -50; i <= 0; i += 1) {
data.push([
time + i * 1000,
Math.round(Math.random() * 100)
]);
}
return data;
}()),
zones: [{
color: '#f8ad40'
}]
}]
};
//end contructor
}
In case you have not been able to resolve this issue, I had the same issue using ion-segment and I was able to resolve it when I replaced ngSwitch with the [hidden] property.
The problem is that the canvas only get rendered once. The canvas is also lost once you switch between your segments, the only solution is to hide you segment when switching between segments
Edit your HTML code to the one below and you should be okay
<div class="segment-chart">
<ion-segment [(ngModel)]="pet">
<ion-segment-button value="dashboard" (ionSelect)="selectedFriends()">
DASHBOARD
</ion-segment-button>
<ion-segment-button value="new">
NEW
</ion-segment-button>
</ion-segment>
</div>
<div >
<div class="chart" [hidden] = "pet != 'dashboard'">
<!--View Chart-->
<div #chart>
<chart type="StockChart" [options]="options"></chart>
</div>
</div>
<ul [hidden] = "pet != 'new'" style="list-style-type:none" class="div-new-body">
<li class="div-new-li" *ngFor="let new of lsNews">
<div class="div-new-detail">
<div class="div-new-title">
{{new.date}}
</div>
<div class="div-new-content">
{{new.title}}
</div>
</div>
<div class="div-new-nav">></div>
</li>
</ul>
</div>
That should solve the issue.

Activeadmin Rails 4 TypeError: $(...).fullCalendar is not a function

Am using rails 4 in my application with active admin gem. i am using fullcalender to show the events.
my code is below index.html.erb
<br />
<div class="link_back">
<%= link_to "Back", meeting_rooms_path, class: "btn-sm btn-primary" %>
</div>
<br />
<%= render 'errors' %>
<p>
<%=link_to 'Create Event', new_event_url(meeting_room_id: params["meeting_room_id"]), :id => 'new_event' %>
</p>
<br />
<!-- <div>
<div class='calendar'></div>
</div> -->
<div>
<div id='calendar'>
</div>
</div>
<div id = "desc_dialog" class="dialog" style ="display:none;">
<div id = "event_desc">
</div>
<br/>
<br/>
<div id = "event_actions">
<span id = "edit_event"></span>
<span id = "delete_event"></span>
</div>
</div>
<div id = "create_event_dialog" class="dialog" style ="display:none;">
<div id = "create_event">
</div>
</div>
<script>
// page is now ready, initialize the calendar...
$('#new_event').click(function(event) {
event.preventDefault();
var url = $(this).attr('href');
$.ajax({
url: url,
beforeSend: function() {
$('#loading').show();
},
complete: function() {
$('#loading').hide();
},
success: function(data) {
$('#create_event').replaceWith(data['form']);
$('#create_event_dialog').dialog({
title: 'New Event',
modal: true,
width: 500,
close: function(event, ui) { $('#create_event_dialog').dialog('destroy') }
});
}
});
});
$('#calendar').fullCalendar({
editable: true,
header: {
left: 'prev,next today',
center: 'title',
right: 'month,agendaWeek,agendaDay'
},
//defaultView: 'agendaWeek',
defaultView: 'month',
height: 500,
slotMinutes: 15,
loading: function(bool){
if (bool)
$('#loading').show();
else
$('#loading').hide();
},
events: "/events/get_events?meeting_room_id=<%= params[:meeting_room_id]%>",
timeFormat: 'h:mm t{ - h:mm t} ',
dragOpacity: "0.5",
eventDrop: function(event, dayDelta, minuteDelta, allDay, revertFunc){
// if (confirm("Are you sure about this change?")) {
moveEvent(event, dayDelta, minuteDelta, allDay);
// }
// else {
// revertFunc();
// }
},
eventResize: function(event, dayDelta, minuteDelta, revertFunc){
// if (confirm("Are you sure about this change?")) {
resizeEvent(event, dayDelta, minuteDelta);
// }
// else {
// revertFunc();
// }
},
eventClick: function(event, jsEvent, view){
if ((<%= current_user.id %>) == event.user_id){
showEventDetails(event);
}
},
});
</script>
the same fullcalender i have used with normal rails 4 applicaiton its working fine.
but with activeadmin its throwing the javascript error as,
TypeError: $(...).fullCalendar is not a function
and the calender is not displaying in view
Because of this error am not able to continue pls help ..
You are not importing fullcalendar js and css files, add these lines
in app/assets/javascripts/active_admin.js.coffee
#= require fullcalendar
in app/assets/javascripts/active_admin.css.scss
#import "fullcalendar"