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;
});
}
Related
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));
}
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>
If i use getcoords() , returns always just the first latitude and longitude for all my array length.
If i use normal syntax {{fire.latitude}},{{fire.longitude}} returns all latitudes and longitudes.
i don't know why, probably a very simple thing
this is the first problem that write, i hope to be was clear.
thanck all for the help .
<template>
<div>
<div v-for="(fire, index) in fires" :key="index">
<!-- {{index}}.{{getCoords()}} -->
{{fire.lat}},{{fire.lon}}
</div>
</div>
</template>
<script>
import axios from 'axios'
export default {
name: 'FiresList',
data () {
return {
fires: null,
errored: false,
}
},
mounted () {
axios.get('https://storage.googleapis.com/public.storykube.com/start2impact/fires.json')
.then(response => {
this.fires = response.data.map((coords)=> {
return {lat: coords.latitude, lon: coords.longitude, date: coords.date}
})
console.log(this.fires);
})
.catch(error => {
console.log(error)
this.errored = true
})
},
methods: {
getCoords (){
for (var latlon in this.fires){
return [this.fires[latlon].lat, this.fires[latlon].lon]
//OTHERWISE
// for (let i = 0; i <= this.fires.length; i++){
// return [this.fires[i].lat, this.fires[i].lon]
}
}
}
}
</script>
<style scoped>
</style>
I think what you are looking for is this:
<div v-for="(fire, index) in fires" :key="index">
{{index}}.{{getCoords(index)}}
</div>
getCoords(index) {
return [this.fires[index].lat, this.fires[index].lon]
}
I'm trying to get Apollo gql to load more posts after clicking a button. So it would load the next 15 results, every time you click - load more.
This is my current code
import Layout from "./Layout";
import Post from "./Post";
import client from "./ApolloClient";
import { useQuery } from "#apollo/react-hooks"
import gql from "graphql-tag";
const POSTS_QUERY = gql`
query {
posts(first: 15) {
nodes {
title
slug
postId
featuredImage {
sourceUrl
}
}
}
}
`;
const Posts = props => {
let currPage = 0;
const { posts } = props;
const { loading, error, data, fetchMore } = useQuery(
POSTS_QUERY,
{
variables: {
offset: 0,
limit: 15
},
fetchPolicy: "cache-and-network"
});
function onLoadMore() {
fetchMore({
variables: {
offset: data.posts.length
},
updateQuery: (prev, { fetchMoreResult }) => {
if (!fetchMoreResult) return prev;
return Object.assign({}, prev, {
posts: [...prev.posts, ...fetchMoreResult.posts]
});
}
});
}
if (loading) return (
<div className="container mx-auto py-6">
<div className="flex flex-wrap">
Loading...
</div>
</div>
);
if (error) return (
<div className="container mx-auto py-6">
<div className="flex flex-wrap">
Oops, there was an error :( Please try again later.
</div>
</div>
);
return (
<div className="container mx-auto py-6">
<div className="flex flex-wrap">
{data.posts.nodes.length
? data.posts.nodes.map(post => <Post key={post.postId} post={post} />)
: ""}
</div>
<button onClick={() => { onLoadMore() }}>Load More</button>
</div>
);
};
export default Posts;
When you click load more it refreshes the query and console errors
Invalid attempt to spread non-iterable instance
I have been loading for solutions but a lot of the examples are previous or next pages like traditional pagination. Or a cursor based infinite loader which I don't want. I just want more posts added to the list onClick.
Any advise is appreciated, thank you.
Your current POSTS_QUERY it isn't accepting variables, so first you need change this:
const POSTS_QUERY = gql`
query postQuery($first: Int!, $offset: Int!) {
posts(first: $first, offset: $offset) {
nodes {
title
slug
postId
featuredImage {
sourceUrl
}
}
}
}
`;
Now, it will use the variables listed in your useQuery and fetchMore.
And to finish the error is because updateQuery isn't correct, change it to:
function onLoadMore() {
fetchMore({
variables: {
offset: data.posts.nodes.length
},
updateQuery: (prev, { fetchMoreResult }) => {
if (!fetchMoreResult) return prev;
return { posts: { nodes: [...prev.posts.nodes, ...fetchMoreResult.posts.nodes] } };
});
}
});
}
I would suggest useState hook to manage a variable that stores current offset in the dataset, place a useEffect to watch changes to that offset, the offset value in passed as query variable to load data. Remove fetchmore, useEffect hook will do the job.
When user clicks on load more button, you just need to update offset value, that will trigger the query and update data.
const [offset,setOffset] = React.useState(0)
const [results, setResults] = React.useState([])
const { loading, error, data } = useQuery(
POSTS_QUERY,
{
variables: {
offset: offset,
limit: 15
},
fetchPolicy: "cache-and-network"
}
);
React.useEffect(() => {
const newResults = [...results, ...data]
setResults(newResults)
}, [data])
function onLoadMore() {
setOffset(results.data.length)
}
hello guys i am fetching data from my api and i try to set a search bar like this :
import React, { Component } from "react";
import ProductsIndex from "./search_bar";
class SearchBar extends Component {
constructor(props) {
super(props);
this.state = { term: "" };
this.onInputChange = this.onInputChange.bind(this);
this.onFormSubmit = this.onFormSubmit.bind(this);
}
onInputChange(event) {
this.setState({ term: event.target.value });
}
onFormSubmit(event) {
event.preventDefault();
ProductsIndex.renderProducts(this.state.term)
// this.props.fetchWeather(this.state.term);
this.setState({ term: "" });
}
render() {
return (
<form onSubmit={this.onFormSubmit} className="input-group">
<input
placeholder="Get a five-day forecast in your favorite cities"
className="form-control"
value={this.state.term}
onChange={this.onInputChange}
/>
<span className="input-group-btn">
<button type="submit" className="btn btn-secondary">Submit</button>
</span>
</form>
);
}
}
export default SearchBar;
In my onSubmit function try to call my function renderProducts from my class ProductsIndex here :
class ProductsIndex extends Component {
componentDidMount() {
this.props.fetchProducts();
}
renderProducts(term) {
return _.map(this.props.products, product => {
if(product.name==term) {
return (
<tr key={product.name}>
<td>{product.name}</td>
<td>{product.product_category.name}</td>
<td>{product.price}</td>
</tr>
);
}
});
}
render(){
return(
<div>
<table className="table table-bordered table-hover table-striped">
<thead>
<tr>
<th>Name</th>
<th>Category</th>
<th>Price</th>
</tr>
</thead>
<tbody>
{this.renderProducts()}
</tbody>
</table>
</div>
);
}
}
function mapStateToProps(state) {
return {products: state.products}
}
export default connect(mapStateToProps, {fetchProducts})
(ProductsIndex);
But that is doesn't work i get this error : Uncaught TypeError: _search_bar2.default.renderProducts is not a function
Thanks you for your help
The short answer is: ProductsIndex.prototype.renderProducts
The correct answer is: the state of terms should be owned by a component higher up in the hierarchy. SearchBar triggers a state change in the parent component and it trickles down to ProductIndex. You already probably have a component suitable for this in your hierarchy, but here is how it may look if you add one explicitly for this purpose.
export class SharedParent extends React.Component {
constructor() {
super();
this.state = {
term: ""
};
//I use this style for methods, you can also use fat arrows to ensure 'this' is properly set
this.setTerm = this.setTerm.bind(this);
}
setTerm(term) {
//beware current best practice is to pass a fucntion
//not an object to setState
this.setState({ term });
}
render() {
return [
<ProductIndex key="a" term={this.state.term} {...this.props.productProps} />,
<SearchBar key="b" setTerm={this.setTerm} {...this.props.searchProps}/>
];
}
}
PS: I would suggest also using Redux or another state management solution. You can't go wrong with Redux though.