Vue.js instant search from API REST Framework using axios - django

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()
}
}

Related

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.

Error: Invalid hook call when using with redux

Sorry if I am asking a beginner's level question. I am new to React.js and recently I have been trying to grasps the concepts by following this tutorial:
JustDjango
What I am trying to accomplish is creating a login form which uses redux to store the states, my code is as follows :
import React from 'react';
import { Form, Icon, Input, Button, Spin } from 'antd/lib';
import { connect } from 'react-redux';
import { NavLink } from 'react-router-dom';
import * as actions from '../store/actions/auth';
const FormItem = Form.Item;
const antIcon = <Icon type="loading" style={{ fontSize: 24 }} spin />;
class NormalLoginForm extends React.Component {
handleSubmit = (e) => {
e.preventDefault();
this.props.form.validateFields((err, values) => {
if (!err) {
this.props.onAuth(values.userName, values.password);
this.props.history.push('/');
}
});
}
render() {
let errorMessage = null;
if (this.props.error) {
errorMessage = (
<p>{this.props.error.message}</p>
);
}
const { getFieldDecorator } = this.props.form;
return (
<div>
{errorMessage}
{
this.props.loading ?
<Spin indicator={antIcon} />
:
<Form onSubmit={this.handleSubmit} className="login-form">
<FormItem>
{getFieldDecorator('userName', {
rules: [{ required: true, message: 'Please input your username!' }],
})(
<Input prefix={<Icon type="user" style={{ color: 'rgba(0,0,0,.25)' }} />} placeholder="Username" />
)}
</FormItem>
<FormItem>
{getFieldDecorator('password', {
rules: [{ required: true, message: 'Please input your Password!' }],
})(
<Input prefix={<Icon type="lock" style={{ color: 'rgba(0,0,0,.25)' }} />} type="password" placeholder="Password" />
)}
</FormItem>
<FormItem>
<Button type="primary" htmlType="submit" style={{marginRight: '10px'}}>
Login
</Button>
Or
<NavLink
style={{marginRight: '10px'}}
to='/signup/'> signup
</NavLink>
</FormItem>
</Form>
}
</div>
);
}
}
const WrappedNormalLoginForm = Form.useForm()(NormalLoginForm);
const mapStateToProps = (state) => {
return {
loading: state.loading,
error: state.error
}
}
const mapDispatchToProps = dispatch => {
return {
onAuth: (username, password) => dispatch(actions.authLogin(username, password))
}
}
export default connect(mapStateToProps, mapDispatchToProps)(WrappedNormalLoginForm);
The error traceback shows that the error is coming from :
76 | const WrappedNormalLoginForm = Form.useForm()(NormalLoginForm);
77 |
78 | const mapStateToProps = (state) => {
79 | return {
Some google search on this particular error shows that this error has something to do with hooks being defined in a classed based component , however i do not understand why :
const mapStateToProps = (state) => {......
is considered a hook
Will greatly appreciate anybody's help!
React hooks only used by functional components. You used class components.
Shortly, Form.useForm() the method is only used functional components, you can read it from this link below:
https://ant.design/components/form/

NuxtJS , Unit Test language picker with Jest and nuxt-i18n

I have a component that switch Language of a nuxtjs application using nuxt-i18n as follows
<template>
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link langpicker">{{ $t("language_picker") }} </a>
<div class="navbar-dropdown is-hidden-mobile">
<div>
<nuxt-link
v-if="currentLanguage != 'en'"
class="navbar-item"
:to="switchLocalePath('en')"
>
<img src="~/static/flags/us.svg" class="flagIcon" /> English
</nuxt-link>
<nuxt-link
v-if="currentLanguage != 'el'"
class="navbar-item"
:to="switchLocalePath('el')"
>
<img src="~/static/flags/el.svg" class="flagIcon" /> Ελληνικά
</nuxt-link>
</div>
</div>
</div>
</template>
<script>
export default {
name: "LangPicker",
computed: {
currentLanguage() {
return this.$i18n.locale || "en";
}
}
};
</script>
I want to write a Unit Test that test the correct language switch on 'nuxt-link' click.
So far I have the following
import { mount, RouterLinkStub } from "#vue/test-utils";
import LangPicker from "#/components/layout/LangPicker";
describe("LangPicker with locale en", () => {
let cmp;
beforeEach(() => {
cmp = mount(LangPicker, {
mocks: {
$t: msg => msg,
$i18n: { locale: "en" },
switchLocalePath: msg => msg
},
stubs: {
NuxtLink: RouterLinkStub
}
});
});
it("Trigger language", () => {
const el = cmp.findAll(".navbar-item")
});
});
cmp.find(".navbar-item") return an empty object.
I don't know how I must set up to "trigger" the click event.
const el = cmp.findAll(".navbar-item")[1].trigger("click");
make sure your find selector is correct.
const comp = cmp.find(".navbar-item");
comp.trigger('click');
you can use chrome dev tools selector utility.
Refer this link for detailed information.

Iterate object list with VueJs from Django API

I try to iterate through a Django Rest API object list using VueJS. I can get the articles json list but I can't correctly view VueJs template component. In my Django views I use
<section class="blog-post-wrap medium-padding80">
<div class="container">
<div class="row">
<div id="starting">
<div class="" v-for="article in articles">
<articlelist title="article.title"></articlelist>
</div>
</div>
</div>
</div>
</section>
And I use VueJs component template
Vue.component('articlelist', {
template: `
<div class="col col-xl-4 col-lg-4 col-md-6 col-sm-12 col-12">
<div class="ui-block"
<div class="post-content">
THE COMMUNITY
{{title}}!
<p>Here’s a photo from last month’s photoshoot. We got really awesome shots for the new catalog.</p>
</div>
<!-- ... end Post -->
</div>
</div>
`,
props: ['title']
});
new Vue({
el: '#starting',
delimiters: ['${','}'],
components:{
'articlelist': articlelist,
},
data: {
articles: [],
loading: false,
currentArticle: {},
message: null,
newArticle: {
'title': null,
'content': null,
},
},
mounted: function() {
this.getArticles();
},
methods: {
getArticles: function() {
this.loading = true;
this.$http.get('/api/articles/')
.then((response) => {
this.articles = response.data;
console.log(response.data);
this.loading = false;
})
.catch((err) => {
this.loading = false;
console.log(err);
})
},
getArticle: function(id) {
this.loading = true;
this.$http.get(`/api/articles/${id}/`)
.then((response) => {
this.currentArticle = response.data;
this.loading = false;
})
.catch((err) => {
this.loading = false;
console.log(err);
})
},
addArticle: function() {
this.loading = true;
this.$http.post(`/api/articles/`,this.newArticle)
.then((response) => {
this.loading = false;
this.getArticles();
})
.catch((err) => {
this.loading = false;
console.log(err);
})
},
updateArticle: function() {
this.loading = true;
this.$http.put(`/api/articles/${this.currentArticle.article_id}/`, this.currentArticle)
.then((response) => {
this.loading = false;
this.currentArticle = response.data;
this.getArticles();
})
.catch((err) => {
this.loading = false;
console.log(err);
})
},
deleteArticle: function(id) {
this.loading = true;
this.$http.delete(`/api/articles/${id}/` )
.then((response) => {
this.loading = false;
this.getArticles();
})
.catch((err) => {
this.loading = false;
console.log(err);
})
}
}
});
I am aware that I could not manage to correct the naming of components and scripts but I can not find the correct way.
When I check chrome dev tools, I see error :
Uncaught ReferenceError: articlelist is not defined
Can you help me ?
Thanks

Vue.js unit test w avoriaz , how to test submit event

I am trying to test a form submit.. it seems that trigger is not appropriate
1) calls store action login when the form is submitted
LoginPage.vue
TypeError: wrapper.find(...).trigger is not a function
at Context.<anonymous> (webpack:///test/unit/specs/pages/LoginPage.spec.js:35:28 <- index.js:50826:29)
my vue component to be tested
LoginPage.vue
<template>
<div class="container">
<div class="login-page">
<h1 class="title">Login to existing account</h1>
<form #submit.prevent="submit()" class="form form--login grid">
<div class="row">
<label for="login__email">Email</label>
<input type="text" id="login__email" class="input--block" v-model="email" v-on:keyup="clearErrorMessage" />
</div>
<div class="row">
<label for="login__password">Password</label>
<input type="password" id="login__password" class="input--block" v-model="password" v-on:keyup="clearErrorMessage" />
</div><!-- /.row -->
<div class="row">
<label></label>
<button id="submit" type="submit">Login</button>
</div><!-- /.row -->
<div v-show='hasError' class="row">
<label></label>
<p class="error">Invalid credentials</p>
</div>
</form>
</div><!-- /.login-page -->
</div>
</template>
<script>
import store from '#/vuex/store'
import { mapActions } from 'vuex'
import _ from 'underscore'
export default {
name: 'loginPage',
data () {
return {
email: 'john.doe#domain.com',
password: 'john123',
hasError: false
}
},
methods: _.extend({}, mapActions(['login']), {
clearErrorMessage () {
this.hasError = false
},
submit () {
return this.login({user: { email: this.email, password: this.password }})
.then((logged) => {
if (logged) {
this.$router.push('shoppinglists')
} else {
this.hasError = true
}
})
}
}),
store
}
</script>
LoginPage.spec.js
import LoginPage from '#/pages/LoginPage'
import Vue from 'vue'
import Vuex from 'vuex'
import sinon from 'sinon'
import { mount } from 'avoriaz'
Vue.use(Vuex)
describe('LoginPage.vue', () => {
let actions
let getters
let store
beforeEach(() => {
actions = {
login: sinon.stub()
}
getters = {
isAuthenticated: () => {
state => state.isAuthenticated
}
}
store = new Vuex.Store({
actions,
getters,
state: {
isAuthenticated: false,
currentUserId: ''
}
})
})
it('calls store action login when the form is submitted', (done) => {
const wrapper = mount(LoginPage, { store })
wrapper.find('#submit').trigger('submit')
wrapper.vm.$nextTick(() => {
expect(actions.login.calledOnce).to.equal(true)
done()
})
})
})
Should be trigger 'click' on the form tag !
it('calls store action login when the form is submitted', (done) => {
const wrapper = mount(LoginPage, { store })
const form = wrapper.find('form')[0]
form.trigger('submit')
wrapper.vm.$nextTick(() => {
expect(actions.login.calledOnce).to.equal(true)
done()
})
})