I have a API that is called when dropdown value changes. It returns JSON results and I would like to update another dropdown from those JSON results but I keep getting an error in my Jquery
Razor View Page
<div class="form-group">
#Html.LabelFor(model => model.CustomerProfile.Country, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(model => model.CustomerProfile.Country, Model.CountryList, htmlAttributes: new { #id = "profileCountry", #class = "form-control col-md-2" , #onchange = "FillState()" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.CustomerProfile.State, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(model => model.CustomerProfile.State, new SelectList(Enumerable.Empty<SelectListItem>(), "StateFullName", "StateFullName"),
"Select State",
htmlAttributes: new { #id = "profileState", #class = "form-control col-md-2" })
</div>
</div>
Jquery Script
<script>
function FillState() {
var countryParam = $('#profileCountry').val();
$.ajax({
url: '/api/CountryToState/FillState',
type: "GET",
dataType: "JSON",
data: { country: countryParam},
success: function (states) {
$("#profileState").html(""); // clear before appending new list
$.each(states, function (i, statetest) {
$("#profileState").append(
$('<option></option>').val(statetest.StateFullName).html(statetest.StateFullName));
});
}
});
}
</script>
API Code
[System.Web.Http.HttpGet]
public ActionResult FillState(string country)
{
var states = _context.CountryToState.Where(c => c.CountryName == country);
return new JsonResult()
{
Data = states,
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
}
CountryToState Model
public class CountryToState
{
[Column("lngStateID")]
[Key]
public Int32 StateID { get; set; }
[Column("strCountry")]
public string CountryName { get; set; }
[Column("strStateFullName")]
public string StateFullName { get; set; }
}
It keeps giving me an error on Cannot read property 'StateFullName' of null. states returned in success function has 36 rows with StateFullName of every row. Why it is null. How can I fix this. I want value and text to be StateFullName in the drop down.
I do not understand the .each function properly
Console.Log(states) show the following:
ContentEncoding: null, ContentType: null, Data: Array(36), JsonRequestBehavior: 0, MaxJsonLength: null, …}
ContentEncoding: null
ContentType: null
Data: (36) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
JsonRequestBehavior: 0
MaxJsonLength: null
RecursionLimit: null
__proto__: Object
I Reviewed your code and I think the error originates from ajax success function
$.ajax({
url: '/api/CountryToState/FillState',
type: "GET",
dataType: "JSON",
data: { country: countryParam},
success: function (states) {
$("#profileState").html(""); // clear before appending new list
$.each(states, function (i, statetest) {
$("#profileState").append(
$('<option></option>').val(statetest.StateFullName).html(statetest.StateFullName));
});
}
});
In the code above I think that state parameter in success callback has such a structure:
{
ContentEncoding: ...
ContentEncoding: ...
ContentType: ...
Data: (36) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…},
{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…},
{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
JsonRequestBehavior: ...
MaxJsonLength: ...
RecursionLimit: ...
}
so you need to make a loop in states.Data instead of states :
$.each(states.Data, function (i, statetest) {
$("#profileState").append(
$('<option></option>').val(statetest.StateFullName).html(statetest.StateFullName));
});
Related
I'm creating a React-native app, and I'm getting some data with AsyncStorage which I transform into an Array with Objects which will be combined/concatnated with data that I fetch from an API
const available_streams = streams.channels.filter(stream => !stream.is_live)
const the_streams = available_streams.map(available_stream => {
available_stream.checked = false
return available_stream
})
console.log(the_streams) :
(3) [{…}, {…}, {…}]
0: {name: "channel1", url: "wss%3A%2F%2Fwebsite.com", app_name: "streams", server_name: "channel1", is_live: false, …}
1: {name: "channel3", url: "wss%3A%2F%2Fwebsite.com", app_name: "streams", server_name: "channel3", is_live: false, …}
2: {app_name: "sms", url: "website.com:4443", server_name: "93b5d83448", name: "Test", is_live: false, …}
length: 3
let saved_stream_names = []
// LOCAL VALUES
get_session_value('url').then(url => {
get_session_object('stream_names')
.then(stream_names => {
stream_names.forEach(name => {
saved_stream_names.push({
name: name,
checked: false,
is_live: false,
url: url
})
})
})
})
Array.prototype.push.apply(saved_stream_names, the_streams)
console.log(saved_stream_names)
the console.log prints the following:
(3) [{…}, {…}, {…}] <---- !! WHY 3 ?!!
0: {name: "channel1", url: "wss%3A%2F%2F.website.com", app_name: "streams", server_name: "channel1", is_live: false, …}
1: {name: "channel3", url: "wss%3A%2F%2Fwebsite.com", app_name: "streams", server_name: "channel3", is_live: false, …}
2: {app_name: "sms", url: "wss://website3:4443/", server_name: "93b5d83448", name: "Test", is_live: false, …}
3: {name: "Xxx", checked: false, is_live: false, url: "https://website.com/"}
4: {name: "Next", checked: false, is_live: false, url: "website.com"}
5: {name: "Arghhj", checked: false, is_live: false, url: "https://website.com/"}
length: 6
also console.log(saved_stream_names.length) says it's 3 in size
and i cannot loop over the last 3 objects. What kind of wizardry is this?
It is a synchronicity problem.
get_session_value('url')
Returns a promise, the code inside the then() will only execute when this promise is solved.
Even tho console.log(saved_stream_names) is the last line of code, it is being executed before the code inside the then(). Try moving the console.log to inside the then:
let saved_stream_names = []
// LOCAL VALUES
get_session_value('url').then(url => {
get_session_object('stream_names')
.then(stream_names => {
stream_names.forEach(name => {
saved_stream_names.push(
({
name: name,
checked: false,
is_live: false,
url: url
})
)
})
console.log(saved_stream_names)
})
})
Array.prototype.push.apply(saved_stream_names, the_streams)
I believe it might be because console.log gets called before get_session_value() finishes running. So when console.log is printed it only contains the values of the_streams. However since it is referencing an array, it will show the whole thing by the time you look at it. You can confirm this by doing console.log(JSON.stringify(saved_stream_names)).
I would suggest you put these lines of code:
Array.prototype.push.apply(saved_stream_names, the_streams)
console.log(saved_stream_names)
inside the get_session_object.then() function.
I have a login form and where the inputs (email & password) are bound. On clicking the button to submit the form, it prevents the default behaviour and uses the login method defined in the Login.vue; Scripts.
While consoling in Login.vue; Scripts; login method, the form data printed out the {email: 'email', password: 'password'} object (desired). Once it is passed to the action (await this.signIn(this.form)), it consoled out a Vue component all of the sudden. I don't understand why this happened and how can this be solved?
Login.vue Component
Form
<form #submit.prevent="login" method="POST">
<input
type="text"
v-model="form.email"
/>
<input
type="password"
v-model="form.password"
/>
<button class="btn btn-primary">Login</button>
</form>
Scripts
<script>
import { mapActions } from 'vuex'
export default {
data() {
return {
form: {
email: '',
password: '',
},
}
},
computed: {
...mapActions('auth', ['signIn']),
},
methods: {
async login() {
/***************************************
* *
* Print out the form data object *
* *
****************************************/
console.log(this.form)
await this.signIn(this.form)
},
},
}
</script>
Vuex - Auth Module
export const actions = {
signIn({ dispatch, commit }, form) {
/***************************************************************
* *
* Print out a Vue component instead of the passed object *
* *
****************************************************************/
console.log(form)
Auth.signInWithEmailAndPassword(form.email, form.password)
.then(user => {
commit('SET_AUTHENTICATED', true)
commit('SET_USER', user.user)
this.$router.push('/')
})
.catch(err => {
console.log(err)
})
},
}
Console logged content
VueComponent {_uid: 4, _isVue: true, $options: {…}, _renderProxy: Proxy, _self: VueComponent, …}
$attrs: (...)
$children: []
$createElement: ƒ (a, b, c, d)
$el: div
$listeners: (...)
$options: {parent: VueComponent, _parentVnode: VNode, propsData: undefined, _parentListeners: undefined, _renderChildren: undefined, …}
$parent: VueComponent {_uid: 3, _isVue: true, $options: {…}, _renderProxy: Proxy, _self: VueComponent, …}
$refs: {}
$root: Vue {_uid: 2, _isVue: true, $options: {…}, _renderProxy: Proxy, _self: Vue, …}
$scopedSlots: {$stable: true, $key: undefined, $hasNormal: false}
$slots: {}
$store: Store {_committing: false, _actions: {…}, _actionSubscribers: Array(1), _mutations: {…}, _wrappedGetters: {…}, …}
$vnode: VNode {tag: "vue-component-4", data: {…}, children: undefined, text: undefined, elm: div, …}
form: (...)
login: ƒ ()
signIn: (...)
__VUE_DEVTOOLS_UID__: "1:4"
_c: ƒ (a, b, c, d)
_computedWatchers: {signIn: Watcher}
_data: {__ob__: Observer}
_directInactive: false
_events: {hook:beforeDestroy: Array(1)}
_hasHookEvent: true
_inactive: null
_isBeingDestroyed: false
_isDestroyed: false
_isMounted: true
_isVue: true
_renderProxy: Proxy {_uid: 4, _isVue: true, $options: {…}, _renderProxy: Proxy, _self: VueComponent, …}
_routerRoot: Vue {_uid: 2, _isVue: true, $options: {…}, _renderProxy: Proxy, _self: Vue, …}
_self: VueComponent {_uid: 4, _isVue: true, $options: {…}, _renderProxy: Proxy, _self: VueComponent, …}
_staticTrees: null
_uid: 4
_vnode: VNode {tag: "div", data: undefined, children: Array(2), text: undefined, elm: div, …}
_watcher: Watcher {vm: VueComponent, deep: false, user: false, lazy: false, sync: false, …}
_watchers: (2) [Watcher, Watcher]
$data: (...)
$isServer: (...)
$props: (...)
$route: (...)
$router: (...)
$ssrContext: (...)
get $attrs: ƒ reactiveGetter()
set $attrs: ƒ reactiveSetter(newVal)
get $listeners: ƒ reactiveGetter()
set $listeners: ƒ reactiveSetter(newVal)
get form: ƒ proxyGetter()
set form: ƒ proxySetter(val)
__proto__: Vue
As Sumurai8 mentioned, I only need to put the ...mapActions('auth', ['signIn']) in methods and not in computed.
methods: {
...mapActions('auth', ['signIn']),
async login() {
console.log(this.form)
await this.signIn(this.form)
},
},
this my code
const fallbackLng = ["en"];
const availableLanguages = ["en", "ru"];
i18n
.use(Backend)
.use(LanguageDetector)
.use(initReactI18next)
.init({
fallbackLng,
debug: true,
whitelist: availableLanguages,
interpolation: {
escapeValue: false
}
});
I see strange lines in the console
i18next::backendConnector: loaded namespace translation for language en {Form: {…}, SignUp: {…}, SignIn: {…}}
i18next::backendConnector: loaded namespace translation for language ru {Form: {…}, SignUp: {…}, SignIn: {…}}
i18next: languageChanged ru
i18next: initialized {debug: true, initImmediate: true, ns: Array(1), defaultNS: Array(1), fallbackLng: Array(1), …}
all code can be seen here codesandbox
Set debug to false in that object
{
fallbackLng,
debug: false,
whitelist: availableLanguages,
interpolation: {
escapeValue: false
}
}
I'm sending request response to app class via redux.
And I receive it in props as I can console.log(this.props.data);
But I get this nested array full of objects
I've tried
console.log(this.props.data[0].PromiseValue);
which results in undefined
[Promise]
0: Promise
__proto__: Promise
[[PromiseStatus]]: "resolved"
[[PromiseValue]]: Object
config: {adapter: ƒ, transformRequest: {…}, transformResponse: {…}, timeout: 0, xsrfCookieName: "XSRF-TOKEN", …}
data: (10) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
headers: {pragma: "no-cache", content-type: "application/json; charset=utf-8", cache-control: "public, max-age=14400", expires: "Mon, 01 Apr 2019 22:25:19 GMT"}
request: XMLHttpRequest {onreadystatechange: ƒ, readyState: 4, timeout: 0, withCredentials: false, upload: XMLHttpRequestUpload, …}
status: 200
statusText: ""
Have you tried to bind to the "then" event of the promise? Something like this:
this.props.data[0].then(value => {
console.log(value);
});
I have this state defined:
constructor(props){
super(props);
this.state = {
open: false,
customers:[],
customer:{},
products:[],
product:{},
orders:[],
order:{},
newForm:true,
phoneNumbererror:null,
shop:this.props.salon,
value:'a',
showTab:'none',
slideIndex: 0,
};
}
With the following function which contains a fetch, I recieve an array of objects with responseData.
getHistory(){
console.log("Log antes del fetch de customer id");
console.log(this.state.customer._id);
fetch(
DOMAIN+'/api/orders/customer/'+this.state.customer._id, {
method: 'get',
dataType: 'json',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization':'Bearer '+this.props.token
}
})
.then((response) =>
{
return response.json();
})
.then((responseData) => {
let orders = responseData.map((order) => {
return order.orderStatusChange ? Object.assign({}, order, {
status: order.orderStatusChange[0].status
}) : order;
});
this.setState({orders:orders});
console.log("Log del responseData");
console.log(responseData);
console.log(responseData.orderStatusChange[0]);
})
.catch(function() {
console.log("error");
});
}
This function is called in handleCellClick, where I pass some data from the consumer, such as the ID:
handleCellClick(y,x,row){
this.setState({
open:true,
slideIndex: 0,
newForm:false,
customer:{...row}
});
this.getProfiles();
this.getHistory();
}
The JSON object obtained from the fetch and kept within this.state.orders looks like this:
(29) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
0:
created:"2017-07-06T15:58:07.958Z"
customer:"59561f3f1d178e1966142ad7"
lastModified:"2017-07-06T15:58:07.958Z"
orderList:[]
orderStatusChange:Array(1)
0:{status: "5", comments: "Creado en back antes de pagar", _id: "595e5e0f60fbf65149916b7c", created: "2017-07-06T15:58:07.958Z"}
length:1
__proto__:Array(0)
shop:"59108159bc3fc645704ba508"
totalAmount:4000
__v:0
_id:"595e5e0f60fbf65149916b7b"
__proto__:Object
As shown previously in the fetch, with this line this.setState({orders:responseData}) I can pass orders to the table where I want the id, date, status and price to be displayed:
<DataTables
height={'auto'}
selectable={false}
showRowHover={true}
columns={HISTORY_TABLE_COLUMNS}
data={this.state.orders}
showCheckboxes={false}
rowSizeLabel="Filas por página"
/>
The table called is:
const HISTORY_TABLE_COLUMNS = [
{
key: '_id',
label: 'Número de pedido',
style:{width: '37%'}
}, {
key: 'created',
label: 'Fecha del pedido',
style:{width: '33%'}
}, {
key: 'status',
label: 'Estado',
style:{width: '13%'}
}, {
key: 'totalAmount',
label: 'Total',
style:{width: '17%'}
}
];
How can I format the price (totalAmount) to have 2 decimals and print next to it the € symbol?
CAPTURE FOR BETTER UNDERSTANDING
This solution works fine with node module material-ui-datatables version 0.18.0
You can use render method in column settings to work on the column data.
const currencyToAppend = '€';
const HISTORY_TABLE_COLUMNS = [
{
....
}, {
....
}, {
key: 'totalAmount',
label: 'Total',
style:{width: '17%'}
render: (amount, all) => {
console.log(amount);
console.log(all);
return amount + ' ' + currencyToAppend;
}
}
];
While iterating data in table please do the following.
totalAmount.toFixed(2) + " €"
Update:
I would suggest this change should be done from backend, But any how for now you can handle it in map iterator where you are setting orders like following
const currencyToAppend = ' €';
let orders = responseData.map((order) => {
return order.orderStatusChange ? Object.assign({}, order, {
status: order.orderStatusChange[0].status
},{
totalAmount: order.totalAmount.toFixed(2) + currencyToAppend
}) : Object.assign({}, order, {
totalAmount: order.totalAmount.toFixed(2) + currencyToAppend
});
});
I hope this will solve your problem.
To complement #dev's answer, I'd suggest to have render the cell as a function as that gives you more control
Check out the codesandox demo https://codesandbox.io/s/0VVwq645L
const HISTORY_TABLE_COLUMNS = [
{
key: "_id",
label: "Número de pedido",
style: { width: "37%" },
value: item =>
<code>
{item._id}
</code>
},
{
key: "created",
label: "Fecha del pedido",
style: { width: "33%" },
value: item => <Time value={item.created} />
},
{
key: "status",
label: "Estado",
style: { width: "13%" },
value: item =>
<span>
{item.status}
</span>
},
{
key: "totalAmount",
label: "Total",
style: { width: "17%" },
value: item => <Amount value={item.totalAmount} currency="€"} />
}
];