Angular search by clicked tag - javascript

In my app I am trying to make a feature that when the user click the tag it shows him all the products that have this tag...
My search request is being made with GET method over an API call... so what I am trying to achieve is that on a tag click the tag value is sent as a parameter in the url and thus returning all products with this tag in a new page... My API call works in POSTMAN but I am having trouble implementing it in Angular...
So my main questions and issues are:
How to make the tag clickable so it sends the value with the api request
How to add routerlink to the tag so it redirects to new page where it shows all the products with this tag
I am very new to Angular so please help :)
This is the image how tags are displayed in the app:
Here is my code:
HTML in home.page.html for outputing the tags:
<ion-chip *ngFor="let tag of tags">
<ion-label>{{ tag.tags }}</ion-label>
</ion-chip>
Code for search API in search.service.ts:
searchByTagCall(tag: string) {
return from(Preferences.get({key: 'TOKEN_KEY'})).pipe(
switchMap(token => {
const headers = new HttpHeaders().set('Authorization', `Bearer ${token.value}`);
let params = new HttpParams();
params = params.append('tag', tag);
return this.httpClient.get(`${environment.apiUrl}search`, {headers, observe: 'response', params});
}),
catchError(err => {
console.log(err.status);
if (err.status === 400) {
console.log(err.error.message);
}
if (err.status === 401) {
this.authService.logout();
this.router.navigateByUrl('/login', {replaceUrl: true});
}
return EMPTY;
}),
);
}
Code of home.page.ts:
searchByTag() {
this.searchService.searchByTagCall(tag).subscribe(
(data: any) => {
/* what do I put here? */
},
error => {
console.log('Error', error);
});
}
My JSON looks like this:
{
"tags": [
{
"tags": "fruit"
},
{
"tags": "sour"
},
{
"tags": "sweet"
},
{
"tags": "frozen"
},
{
"tags": "fresh"
},
{
"tags": "vegetable"
},
{
"tags": "raw"
}
]
}

Do the following changes:
home.page.html:
<ion-chip *ngFor="let tag of tags">
<ion-label class="tag" (click)="searchByTag(tag.tags)">{{ tag.tags }}</ion-label>
</ion-chip>
home.page.scss:
// In case you need to change the cursor to be the hand icon
.tag {
cursor: pointer;
}
home.page.ts:
constructor(/*whatever parameters you have in your constructor already*/, private router: Router, private dataService: DataService) {
// Whatever code you have in your constructor
}
searchByTag(tag: string) {
this.searchService.searchByTagCall(tag).subscribe((data: any) => {
// 1. Store the data in some service so it will be accessible for the other page
this.dataService.tagData = data;
// 2. Navigate to the other page (you can store the tag in the route params if you need it)
this.router.navigate(['/tagPage/', tag]);
},
error => {
console.log('Error', error);
});
}

Related

Cypress: extract value from JSON body to a variable

I am using cypress.io to test an API(Created using Node.js). I want to extract value from JSON response of the API and save it to a variable.
I have tried solutions mentioned in the below links it did not work:
Cypress - get value from json response body
Below is the code i am using to test the API:
/// <reference types="cypress" />
describe("Testing Provider Catalog API", () => {
it("Provider Catalog API GET Request", () => {
cy.request('GET', 'v1/providers')
.then((response) => {
expect(response).to.have.property('status', 200)
expect(response.body).to.not.be.null
// expect(response.body.providers).to.have.length(1)
})
cy.screenshot()
})
it("Provider Catalog API POST Request", () => {
const provider = {
"type": "loadboard",
"name": "123loadboard"
};
cy.request('POST', 'v1/providers', provider)
cy.screenshot()
})
it("Provider Catalog API PUT Request", () => {
const provider = {
"type": "loadboard",
"name": "123loadboard"
};
cy.request('PUT', 'v1/providers/61a54a66a2b734859481931c', provider)
cy.screenshot()
})
it("Provider Catalog API DELETE Request", () => {
cy.request('DELETE', 'v1/providers/61a68e7ca6011e605029191b')
cy.screenshot()
})
})
Below is the code that i am using
var providerId
cy.wait('#newObject').then((response) => {
expect(response.status).to.eq(201)
expect(response.responseBody).to.have.property('_id')
const body = (response.responseBody)
providerId = body['_id']
})
cy.get(someInput).type(newId)
Sample output of the API:
{
"_id":"61a54ba1a2b7348594819323",
"type":"loadboard",
"name":"123loadboard",
"__v":0
}
I want to store the value of the _id in a variable and use it later. I have been trying to for the last couple of days and nothing seems to work. Can anyone please help me. Thanks in advance.
The recommended way to use variables with cypress is with aliases. See docs here.
In your code, you can wrap() your _id and save it as an alias, then call your alias somewhere else in your code:
cy.wait('#newObject').then((response) => {
expect(response.status).to.eq(201)
expect(response.responseBody).to.have.property('_id')
cy.wrap(response.responseBody._id).as('newId')
})
// then later in your code use:
cy.get('#newId').then((newId) => {
cy.get(someInput).type(newId)
})
You could also type() your _id inside your wait():
cy.wait('#newObject').then((response) => {
expect(response.status).to.eq(201)
expect(response.responseBody).to.have.property('_id')
cy.get(someInput).type(response.responseBody._id)
})
Or you can use cypress global environmment object Cypress.env(). See docs here.
cy.wait('#newObject').then((response) => {
expect(response.status).to.eq(201)
expect(response.responseBody).to.have.property('_id')
Cypress.env('newId', response.responseBody._id)
})
// then later in your code use:
cy.get(someInput).type(Cypress.env('newId'))

Handling file upload in Sharepoint List with React form

I am creating a form webpart with react but I am stuck at uploading a file, So I want to upload a file when it hit submit button and that has to create sharepoint list item with attachment file.
above image is for reference to understand.
Now I am able to create Above two Subjects and comments in sharepoint list but unsure for upload to attach with same list item as attachments.
<div className={styles.row}>
<ReactFileReader fileTypes={[".csv", ".xlsx", ".Docx", ".pdf"]} base64={true} handleFiles={this.handleFiles.bind(this)}>
<button className='btn' value={this.state.UploadedFilesArray.toString()} >Upload</button>
</ReactFileReader>
</div>
<div className={styles.row}>
<div >
<button id="btn_add" className={styles.button} onClick={this.createItem.bind(this)}>Submit</button>
</div>
The above code is for Upload and Submit, As I said I want to attach the attachment when I submit the form.
private createItem(): void {
this.setState({
status: 'Creating item...',
items: []
});
const body: string = JSON.stringify({
'Title': this.state.subject,
'Comments': this.state.comments,
});
this.props.spHttpClient.post(`${this.props.siteUrl}/_api/Web/lists/getbytitle('${this.props.listName}')/items`,
SPHttpClient.configurations.v1,
{
headers: {
'Accept': 'application/json;odata=nometadata',
//"Accept": "application/json; odata=verbose",
'Content-type': 'application/json;odata=nometadata',
'odata-version': ''
},
body: body
})
.then((response: SPHttpClientResponse): Promise<IListItem> => {
return response.json();
console.log(response)
})
.then((item: IListItem): void => {
this.setState({
status: `Item '${item.Title}' (ID: ${item.Id}) successfully created`,
items: []
});
}, (error: any): void => {
this.setState({
status: 'Error while creating the item: ' + error,
items: []
});
});
}
The above code is for handling the submit, Now can anyone help me creating the handle file function with the functionality that I want to create a sharepoint list item with an attachment when I hit the Submit button. Also with Success or Error message.
I suggest you use this awesome library PNPJS library, it will be easy to work with attachments.
Use (PnPJS) library with SharePoint Framework web parts
Add attachments after item created
private handleFiles(f) {
var filelist = f.fileList;
var fileInfos: IAttachmentFileInfo[] = [];
fileInfos.push({
name: "My file name 1",
content: "string, blob, or array"
});
// loop through files
for (var i = 0; i < filelist.length; i++) {
// get item
let file: File = filelist.item(i);
fileInfos.push({
name: file.name,
content: file
});
}
this.setState({
uploadfiles: fileInfos
});
}
private createItem(): void {
sp.web.lists.getByTitle("mylist").items.add({
'Title': this.state.subject
}).then((r: IItemAddResult) => {
r.item.attachmentFiles.addMultiple(this.state.uploadfiles);
}).then(e => {
console.log("successfully created");
}).catch(e => {
console.log("Error while creating the item" + e)
});
}
For more details, please refer to below demo:
SharedSPFx

Searching through API in Vuetify autocomplete search is very slow

I'm using the Vuetify Autocomplete component to search through all stock symbols and company names (117230 in total). When I enter a search term, the browser becomes very laggy for a few seconds. When I tried it with few records (6000), there were no problems. The API is hosted locally for now.
I am assuming this job should be done by the backend? But I'm not sure how. What are my options?
Javascript code in the search component:
import Chart from './GChart.vue'
export default {
name: "Search",
components: {
Chart,
},
data: () => ({
symbolsExchangesNames: [],
isLoading: false,
model: null,
search: null
}),
computed: {
items () {
return this.symbolsExchangesNames
}
},
watch: {
search (val) {
console.log(val)
if (this.items.length > 0) return
if (this.isLoading) return
this.isLoading = true
fetch('http://localhost/API/allSymbolsExchangesNames')
.then(res => res.json())
.then(res => {
for(let i of res){
this.symbolsExchangesNames.push({
Code: i.Symbol,
Exchange: i.Exchange,
Name: i.Name,
CodeAndName: `${i.Symbol} ${i.Name}`
})
}
})
.catch(err => {
console.log(err)
})
.finally(() => (this.isLoading = false))
}
}
}
This is how the data looks like:
[{"Symbol": "A", "Exchange": "US", "Name": "Agilent Technologies, Inc"}, {"Symbol": "AA", "Exchange": "US", "Name": "Alcoa Corporation"},...]
117k records takes sometime to be rendered in HTML.
I'd suggest you to use some debounce function (in the example below I used underscore), to only query your backend when the user stops typing or you could use some real type of input, like pressing enter in the input or submitting the form.
And you should prevent the backend from returning so many records, so yeah, you should filter results in your backend, it's usually used query string on GET requests for this (https://www.google.com/search?q=text%20to%20search)
new Vue({
el: "#app",
data() {
return {
query: "",
data: []
}
},
methods: {
// with debounce, 300ms after user stops typing, the callback will be executed
search: _.debounce(function() {
// here you should query your backend, something like
// http://localhost/API/allSymbolsExchangesNames?query=#{this.query}
this.data = [1,2,3]
}, 300)
}
})
<script src="https://underscorejs.org/underscore-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<input v-model="query" #keypress="search" />
{{data}}
</div>

I am trying to compare the retrieved tags with selected or created tags, if tag doesn't exist perfom post request

I have an object that contains some tags, I retrieved the list of tags to choose from but I want to create some tags before submitting the form. I am trying to use vue watch but it seems not working.
here is the code:
data() {
return {
blog: {
blog_title: "",
tags: "",
text: "",
},
myTags: {}
};
},
watch: {
blog: {
deep: true,
handler: function(tag) {
let token = localStorage.getItem("TOKEN_STORAGE_KEY");
for(tag of this.blog.tags) {
if (this.myTags.includes(tag)) {
return axios.post(API_URL + "tags/", tag, {headers: { Authorization: `Token ${token}` }})
}
}
}
note: I am using vuetify combobox:
<v-combobox label="Tags" :multiple="true" :return-object="false" :items="myTags" item-text="title" chips v-model="blog.tags"></v-combobox>
I am expecting to perform post request once I hit enter for new tag.
1st - handler: function(tag) - tag here is not tag - it is the new value of blog - the property you set watch for.
2nd - for(tag of this.blog.tags) you use your inbound argument to iterate over blog.tags. It is probably not what you want.
So, the resulting code that should work:
watch: {
blog: {
deep: true,
handler: function(newVal) {
let token = localStorage.getItem("TOKEN_STORAGE_KEY");
for(let tag of newVal.tags) {
if (this.myTags.includes(tag)) {
return axios.post(API_URL + "tags/", tag, {headers: { Authorization: `Token ${token}` }})
}
}
}
I refactored my tag model, I was using django-category package, I replaced it with the new model, I get rid of the watch garbage code and it worked perfectly.
if you pass by this question or problem, I used
class CustomSlugRelatedField(serializers.SlugRelatedField):
def to_internal_value(self, data):
try:
obj, created = self.get_queryset().get_or_create(**{self.slug_field: data})
return obj
except (TypeError, ValueError):
self.fail('invalid')
as mentioned in this answer

Getting wrapped data via Vue-Laravel API Resource connection

I'm using Laravel and Vue connected via API,
Everything works fine.
I asked to get offer via method from Vue:
getOffer(id) {
this.$http.get('http://127.0.0.1:8000/api/offers/'+id)
.then(response => response.json())
.then(result => this.offer = result)
}
},
And I recived this:
{
"body": "xx"
"title": "yy"
}
and then put it into offer variable:
data() {
return {
offer: {
title:'',
body:''
}
}
},
and I used it into template
<div>
<h3 class="headline mb-0">{{offer.title}}</h3>
<div>
<br>
{{offer.body}}</div>
</div>
easy, all works fine
Now I decided to use Laravel Resource. This is wrapping data into "data" object within json response, so I got now this:
{
"data": {
"body": "xx"
"title": "yy"
}
}
and my template is blank - can anyone tell me how should I change the code, to work with new data object? And how I could work with it, when It will contain more objects, like:
{
"data": {
"body": "xx"
"title": "yy"
},
"data2":{
"body": "xx"
"title": "yy"
},
}
etc.
getOffer function should be modified to use result.data instead of raw result:
getOffer(id) {
this.$http.get('http://127.0.0.1:8000/api/offers/'+id)
.then(response => response.json())
.then(result => this.offer = result.data)
}
},
now it works again

Categories