I need to Delete any object from my DRF / django connected database through angular 13 - django

I am getting the id which i have to delete but the last line of service.ts that is of delete method is not getting executed...
the files and code snippets I used are : -
COMPONENT.HTML
<li *ngFor="let source of sources$ | async | filter: filterTerm">
<div class="card">
<div class="card-body">
<h5 class="card-title">{{source.name}}</h5>
<p>URL:- <a href ='{{source.url}}'>{{source.url}}</a></p>
<a class="btn btn-primary" href='fetch/{{source.id}}' role="button">fetch</a>
<button class="btn btn-primary" (click)="deleteSource(source.id)">delete </button>
<br>
</div>
</div>
</li>
I tried to console the id geeting from html and the Id i am getting is correct.
//component.ts
export class SourcesComponent implements OnInit {
filterTerm!: string;
sources$ !: Observable<sources[]>;
// deletedSource !: sources;
constructor(private sourcesService: SourcesService) { }
// prepareDeleteSource(deleteSource: sources){
// this.deletedSource = deleteSource;
// }
ngOnInit(): void {
this.Source();
}
Source(){
this.sources$ = this.sourcesService.Sources()
}
deleteSource(id : string){
console.log(id)
this.sourcesService.deleteSource(id);
}
//service.ts
export class SourcesService {
API_URL = 'http://127.0.0.1:8000/sourceapi';
constructor(private http: HttpClient) { }
// let csrf = this._cookieService.get("csrftoken");
// if (typeof(csrf) === 'undefined') {
// csrf = '';
// }
/** GET sources from the server */
Sources() : Observable<sources[]> {
return this.http.get<sources[]>(this.API_URL,);
}
/** POST: add a new source to the server */
addSource(source : sources[]): Observable<sources[]>{
return this.http.post<sources[]> (this.API_URL, source);
//console.log(user);
}
deleteSource(id: string): Observable<number>{
let httpheaders=new HttpHeaders()
.set('Content-type','application/Json');
let options={
headers:httpheaders
};
console.log(id)
return this.http.delete<number>(this.API_URL +'/'+id)
}
}

Angular HTTP functions return cold observables. This means that this.http.delete<number>(this.API_URL +'/'+id) will return an observable, which will not do anything unless someone subscribes to it. So no HTTP call will be performed, since no one is watching the result.
If you do not want to use the result of this call, you have different options to trigger a subscription.
simply call subscribe on the observable:
deleteSource(id : string){
console.log(id)
this.sourcesService.deleteSource(id).subscribe();
}
Convert it to a promise and await it (or don't, if not needed) using lastValueFrom:
async deleteSource(id : string){
console.log(id)
await lastValueFrom(this.sourcesService.deleteSource(id));
}

Related

Lit2.0 how to submit form data to backend

Am using Lit2.0, Material Web components, Django (backend).
one reference: https://www.thinktecture.com/en/web-components/flaws/
I don't understand how to submit form data from Lit component to backend (Django)
form.html contains Lit component (basic-form)
<form id="id_demo" method="post" action="">
{% csrf_token %}
<basic-form></basic-form>
<button type="submit" class="mdc-button mdc-button--raised">Submit</button>
</form>
basic-form is a Lit component and it contains Material web components
import {LitElement, html} from "lit";
// const template = document.createElement('template');
// template.innerHTML = `
// <slot></slot>
// `;
export class BasicForm extends LitElement {
static properties = {
form: '',
};
constructor() {
super();
// this.shadow = this.attachShadow({ mode: 'open' });
// this.shadow.appendChild(template.content.cloneNode(true));
}
render() {
return html`
<mwc-textfield name="first_name"></mwc-textfield>
`;
}
}
customElements.define('basic-form', BasicForm);
Could someone guide me to the right direction.
You can take the value of textfield element on blur and save it as property of basic-form. Then on form submit you can take the basic-form.value property:
basic-form
export class BasicForm extends LitElement {
static properties = {
value: ''
}
onBlur() {
this.value = e.target.value;
}
render() {
return html`
<mwc-textfield name="first_name" #onBlur="${this.onBlur}></mwc-textfield>
`;
}
form.html
<form id="id_demo" method="post" action="">
{% csrf_token %}
<basic-form></basic-form>
<button type="submit" class="mdc-button mdc-button--raised">Submit</button>
</form>
<script>
const form = document.getElementById("id_demo");
const basicForm = form.querySelector('basic-form');
const onSubmit = (event) => {
console.log(basicForm.value);
}
form.addEventListener('submit', onSubmit);
</script>
Actually this is not that easy at all. You have to use ElementInternals: https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals
"The ElementInternals interface of the Document_Object_Model gives web developers a way to allow custom elements to fully participate in HTML forms."
That said here is an example:
<script type="module">
import {
LitElement,
html,
css
} from "https://unpkg.com/lit-element/lit-element.js?module";
class MyItem extends LitElement {
static get formAssociated() {
return true;
}
static get properties() {
return {
name: { type: String, reflect: true },
required: { type: Boolean, reflect: true },
value: { type: String }
};
}
constructor() {
super();
this.internals = this.attachInternals();
this.name = name;
this.required = false;
this.value = '';
this._required = false;
}
render() {
return html`
<label for="input"><slot></slot></label>
<input type="${this.type}" name="${this.name}" id="input" .value="${this.value}" ?required="${this.required}" #input="${this._onInput}">
`;
}
_onInput(event) {
this.value = event.target.value
this.internals.setFormValue(this.value);
}
firstUpdated(...args) {
super.firstUpdated(...args);
/** This ensures our element always participates in the form */
this.internals.setFormValue(this.value);
}
}
customElements.define("my-item", MyItem);
const formElem = document.querySelector('#formElem');
formElem.addEventListener('submit', event => {
event.preventDefault();
const form = event.target;
/** Get all of the form data */
const formData = new FormData(form);
formData.forEach((value, key) => console.log(`${key}: ${value}`));
});
</script>
<form id="formElem">
<my-item name="firstName">First name</my-item>
<button type="submit">Submit</button>
</form>

Getting credit card brand and show error message when using hosted fields in Braintree

I am trying to create payment page using braintree's hosted fields.
I have created sandbox account.
But i am not getting additional details like Card brand, error message like Drop in UI.
How to get those functionalities using Hosted fields.
import React from 'react';
var braintree = require('braintree-web');
class BillingComponent extends React.Component {
constructor(props) {
super(props);
this.clientDidCreate = this.clientDidCreate.bind(this);
this.hostedFieldsDidCreate = this.hostedFieldsDidCreate.bind(this);
this.submitHandler = this.submitHandler.bind(this);
this.showPaymentPage = this.showPaymentPage.bind(this);
this.state = {
hostedFields: null,
errorOccurred: false,
};
}
componentDidCatch(error, info) {
this.setState({errorOccurred: true});
}
componentDidMount() {
this.showPaymentPage();
}
showPaymentPage() {
braintree.client.create({
authorization: 'sandbox_xxxxx_xxxxxxx'
}, this.clientDidCreate);
}
clientDidCreate(err, client) {
braintree.hostedFields.create({
onFieldEvent: function (event) {console.log(JSON.stringify(event))},
client: client,
styles: {
'input': {
'font-size': '16pt',
'color': '#020202'
},
'.number': {
'font-family': 'monospace'
},
'.valid': {
'color': 'green'
}
},
fields: {
number: {
selector: '#card-number',
'card-brand-id': true,
supportedCardBrands: 'visa'
},
cvv: {
selector: '#cvv',
type: 'password'
},
expirationDate: {
selector: '#expiration-date',
prefill: "12/21"
}
}
}, this.hostedFieldsDidCreate);
}
hostedFieldsDidCreate(err, hostedFields) {
let submitBtn = document.getElementById('my-submit');
this.setState({hostedFields: hostedFields});
submitBtn.addEventListener('click', this.submitHandler);
submitBtn.removeAttribute('disabled');
}
submitHandler(event) {
let submitBtn = document.getElementById('my-submit');
event.preventDefault();
submitBtn.setAttribute('disabled', 'disabled');
this.state.hostedFields.tokenize(
function (err, payload) {
if (err) {
submitBtn.removeAttribute('disabled');
console.error(err);
}
else {
let form = document.getElementById('my-sample-form');
form['payment_method_nonce'].value = payload.nonce;
alert(payload.nonce);
// form.submit();
}
});
}
render() {
return (
<div className="user-prelogin">
<div className="row gutter-reset">
<div className="col">
<div className="prelogin-container">
<form action="/" id="my-sample-form">
<input type="hidden" name="payment_method_nonce"/>
<label htmlFor="card-number">Card Number</label>
<div className="form-control" id="card-number"></div>
<label htmlFor="cvv">CVV</label>
<div className="form-control" id="cvv"></div>
<label htmlFor="expiration-date">Expiration Date</label>
<div className="form-control" id="expiration-date"></div>
<input id="my-submit" type="submit" value="Pay" disabled/>
</form>
</div>
</div>
</div>
</div>
);
}
}
export default BillingComponent;
I am able to get basic functionalities like getting nonce from card details. But i am unable to display card brand image/error message in the page as we show in Drop in UI.
How to show card brand image and error message using hosted fields?
Page created using Hosted fields:
Page created Drop in UI - Which shows error message
Page created Drop in UI - Which shows card brand
Though we do not get exact UI like Drop in UI, we can get the card type and display it ourselves by using listeners on cardTypeChange.
hostedFieldsDidCreate(err, hostedFields) {
this.setState({hostedFields: hostedFields});
if (hostedFields !== undefined) {
hostedFields.on('cardTypeChange', this.cardTypeProcessor);
hostedFields.on('validityChange', this.cardValidator);
}
this.setState({load: false});
}
cardTypeProcessor(event) {
if (event.cards.length === 1) {
const cardType = event.cards[0].type;
this.setState({cardType: cardType});
} else {
this.setState({cardType: null});
}
}
cardValidator(event) {
const fieldName = event.emittedBy;
let field = event.fields[fieldName];
let validCard = this.state.validCard;
// Remove any previously applied error or warning classes
$(field.container).removeClass('is-valid');
$(field.container).removeClass('is-invalid');
if (field.isValid) {
validCard[fieldName] = true;
$(field.container).addClass('is-valid');
} else if (field.isPotentiallyValid) {
// skip adding classes if the field is
// not valid, but is potentially valid
} else {
$(field.container).addClass('is-invalid');
validCard[fieldName] = false;
}
this.setState({validCard: validCard});
}
Got the following response from braintree support team.
Hosted fields styling can be found in our developer documentation. Regarding the logos, you can download them from the card types official websites -
Mastercard
Visa
AMEX
Discover
JCB
Or online from other vendors.
Note: Drop-In UI will automatically fetch the brand logos and provide validation errors unlike hosted fields as it is less customizable.

SPFx - having trouble read person field from SP list

I'm trying to get data from person field in SharePoint. My code always returns 8 rows (its correct) but at items that consists of Person it returns [object Obejct].
enter image description here
export interface SPUser {
Pracovnik: String;
}
.
.
private getListData(): void {
this._getListData().then((response) => {
this._renderList(response);
});
}
private _renderList(items: SPUser[]): void {
let html: string = '<table class="TFtable" border=1 width=100% style="border-collapse: collapse;">';
html += `<th>Title</th>`;
items.forEach((item: SPUser) => {
if(item.Pracovnik != null) {
html += `
<tr> <td>${item.Pracovnik}</td> </tr>
`};
});
html += `</table>`;
const listContainer: Element = this.domElement.querySelector('#spGetListItems');
listContainer.innerHTML = html;
}
private async _getListData(): Promise<SPUser[]> {
return pnp.sp.web.lists.getByTitle("org_struktura").items.select("Pracovnik/ID").expand("Pracovnik").get().then((response) => {
return response;
});
}
public render(): void {
this.domElement.innerHTML = `
<div class="parentContainer" style="background-color: lightgrey">
<div style="background-color: lightgrey" id="spGetListItems" />
</div>
`;
this.getListData();
}
Any idea what is wrong please?
Sample demo to map SharePoint list item to a Type object.
export interface IReactItem{
Id:number,
Title:string,
Description:string,
User:{ID:number,EMail:string},
enableEdit:boolean
}
private async _getListData(): Promise<IReactItem[]> {
return pnp.sp.web.lists.getByTitle("TestList").items.select("Id,Title,Description,User/ID,User/EMail").expand("User").get().then((response:IReactItem[]) => {
return response;
});
}

map is not a function

I am trying to make an website that connets with a weather api and gets some info about current weather in given city. When i looked at network flow in my application i do recive a json that contais that information but i cannot map it and display results. Error i revice is :
TypeError: response.json(...).map is not a function WeatherSearchComponent.ts:138
at MapSubscriber.project (WeatherSearchComponent.ts:84)
at MapSubscriber._next (map.js:77)
at MapSubscriber.Subscriber.next (Subscriber.js:89)
at XMLHttpRequest.onLoad (http.umd.js:1083)
at ZoneDelegate.invokeTask (zone.js:225)
at Object.onInvokeTask (core.umd.js:6004)
at ZoneDelegate.invokeTask (zone.js:224)
at Zone.runTask (zone.js:125)
at XMLHttpRequest.ZoneTask.invoke (zone.js:293)
Heres WeatherSearchComponent:
import {
Component,
Injectable,
OnInit,
ElementRef,
EventEmitter,
Inject
} from '#angular/core';
import { Http, Response } from '#angular/http';
import { Observable } from 'rxjs';
import 'rxjs/Rx';
import 'rxjs/add/operator/map';
export var WEATHER_API_KEY: string = 'api_key';
export var WEATHER_API_URL: string ='http://api.openweathermap.org/data/2.5/weather';
export var GDANSK_ID: string = '3099434';
/*let loadingGif: string = ((<any>window).__karma__) ? '' :
require('images/loading.gif');*/
class SearchResult {
content: string;
constructor(obj?: any) {
this.content= obj && obj.content ||
WEATHER_API_URL + 'id=' + obj.city + 'appid=' + WEATHER_API_KEY;
}
}
// http://api.openweathermap.org/data/2.5/weather?
// id=3099434&appid=api_key
#Injectable()
export class WeatherService {
constructor(private http: Http,
#Inject(WEATHER_API_KEY) private apiKey: string,
#Inject(WEATHER_API_URL) private apiUrl: string) {
}
search(city: string): Observable<SearchResult[]> {
let params: string = [
`q=${city}`,
`appid=${this.apiKey}`
].join('&');
let queryUrl: string = `${this.apiUrl}?${params}`;
console.log("query url" , queryUrl);
var getRequest = this.http.get(queryUrl);
console.log("getRequest", getRequest);
return getRequest
.map((response: Response) => {
return (<any>response.json()).map(item => {
console.log("raw item", item); // uncomment if you want to debug
return new SearchResult({
content: item.main.temp
});
});
});
}
}
export var weatherServiceInjectables: Array<any> = [
{provide: WeatherService, useClass: WeatherService},
{provide: WEATHER_API_KEY, useValue: WEATHER_API_KEY},
{provide: WEATHER_API_URL, useValue: WEATHER_API_URL}
];
/**
* SearchBox displays the search box and emits events based on the results
*/
#Component({
outputs: ['loading', 'results'],
selector: 'search-box',
template: `
<input type="text" class="form-control" placeholder="Search" autofocus>
`
})
export class SearchBox implements OnInit {
loading: EventEmitter<boolean> = new EventEmitter<boolean>();
results: EventEmitter<SearchResult[]> = new EventEmitter<SearchResult[]> ();
constructor(private weather: WeatherService,
private el: ElementRef) {
}
ngOnInit(): void {
// convert the `keyup` event into an observable stream
var observable =
Observable.fromEvent(this.el.nativeElement, 'keyup');
observable
.map((e: any) => e.target.value) // extract the value of the input
.filter((text: string) => text.length > 1) // filter out if empty
.debounceTime(250) // only once every 250ms
.do(() => this.loading.next(true)) // enable loading
// search, discarding old events if new input comes in
.map((query: string) => this.weather.search(query))
.switch()
// act on the return of the search
.subscribe(
(results: SearchResult[]) => { // on sucesss
this.loading.next(false);
this.results.next(results);
},
(err: any) => { // on error
console.log(err);
this.loading.next(false);
},
() => { // on completion
this.loading.next(false);
}
);
}
}
#Component({
inputs: ['result'],
selector: 'search-result',
template: `
<div class="col-sm-6 col-md-3">
<div class="thumbnail">
<img src="{{result.thumbnailUrl}}">
<div class="caption">
<h3>{{result}}</h3>
<p>{{result}}</p>
<p><a href="{{result.videoUrl}}"
class="btn btn-default" role="button">Watch</a></p>
</div>
</div>
</div>
`
})
export class SearchResultComponent {
result: SearchResult;
}
#Component({
selector: 'weather-search',
template: `
<div class='container'>
<div class="page-header">
<h1>Weather Search</h1>
</div>
<div class="row">
<div class="input-group input-group-lg col-md-12">
<search-box
(loading)="loading = $event"
(results)="updateResults($event)"
></search-box>
</div>
</div>
<div class="row">
<search-result
*ngFor="let result of results"
[result]="result">
</search-result>
</div>
</div>
`
})
export class WeatherSearchComponent {
results: SearchResult[];
updateResults(results: SearchResult[]): void {
this.results = results;
// console.log("results:", this.results); // uncomment to take a look
}
}
This is what i do recive from api call :
/*
{"coord":{"lon":18.65,"lat":54.35},
"weather":[{"id":801,"main":"Clouds",
"description":"few clouds","icon":"02n"}],"base":"stations",
"main":{"temp":270.15,"pressure":1035,"humidity":92,
"temp_min":270.15,"temp_max":270.15},"visibility":10000,
"wind":{"speed":2.1,"deg":290},"clouds":{"all":20},
"dt":1484665200,
"sys":{"type":1,"id":5349,"message":0.0029,
"country":"PL","sunrise":1484636080,"sunset":1484665040},"
id":3099434,"name":"Gdansk","cod":200}
*/
It's not clear from your question whether you're trying to .map() on a variable or on an observable.
Scenario #1: variable.map()
variable must be an array. Looks from your code that your variable contains an object.
const jsonData = {
"coord":{"lon":18.65,"lat":54.35},
// Abridged for brevity...
id":3099434,"name":"Gdansk","cod":200
};
jsonData.map(...); // WON'T WORK - `jsonData` must be an array
Scenario #2: observable.map()
You must import the map operator in your code before mapping:
import 'rxjs/add/operator/map';
// Then, later
Observable.map(...);

AngularJS with list

I'm doing an employees list that is loaded from SQLite database. I don't know why my list is empty, but I can see via JSON.stringify that data is comming.
I'm using AngularJS and Cordova Framework. Debugging with Ripple.
listEmployees.html
<div data-role="page" apply-jq-mobile>
<div data-theme="a" data-role="header">
<h3>
Header
</h3>
</div>
<div data-role="content">
<div class="ui-grid-a">
<div class="ui-block-a">
</div>
<div class="ui-block-b">
<a data-role="button" href="#/new" data-icon="plus" data-iconpos="left">
New
</a>
</div>
</div>
<input type="text" ng-model="search" class="search-query" placeholder="Search">
<ul data-role="listview" data-divider-theme="b" data-inset="true">
<li data-role="list-divider" role="heading">
Employees
</li>
<li data-theme="c" ng-repeat="employee in employees">
{{employee.name}}
</li>
</ul>
</div>
</div>
EmployeeCtrl
function EmployeeCtrl($scope, Employee){
$scope.employees = Employee.getAllEmployees();
$scope.saveEmployee = function(id) {
if(id){
//TODO
} else {
Employee.addEmployee($scope.employee);
}
}
}
Employee
app.factory('Employee', function() {
var data = {};
data.addEmployee = function(_employee, callback) {
var employee = new Employee();
employee = _employee;
DbService.db.employees.add(employee);
DbService.db.saveChanges(callback);
}
data.getAllEmployees = function() {
DbService.db.employees.toArray(function(employees) {
console.log(JSON.stringify(employees));
return employees;
});
};
return data;
});
I think you need to use promises. Your call to db is asynchronous and therefore view is getting rendered before your data arrives.
data.getAllEmployees = function() {
var $d = $q.defer();
DbService.db.employees.toArray(function(employees) {
console.log(JSON.stringify(employees));
$d.resolve(employees);
});
return $d.promise;
};
Angular templating system wait for all the promises to get resolved before rendering. So I think this will solve your problem.
So, I solved it.
function EmployeeCtrl($scope, Employee){
Employee.getAllEmployees(function(employees){
$scope.employees = employees;
$scope.$apply();
});
}
.
app.factory('Employee', function() {
var data = {};
data.getAllEmployees = function(callback) {
DbService.db.employees.toArray(function(employees) {
callback(employees);
});
};
return data;
});
Thank you all!