Displaying Bookshelf.js model in hbs view - express

I'm new to Bookshelf.js and hbs and would appreciate your help with the following:
I want to pre-fill a handlebars.js view form with data from a Bookshelf.js model.
hbs won't let me use the Bookshelf get method to get the desired attribute value: e.g. {{user.get(age)}}
I converted the model to JSON but am not able to query a specific json attribute in hbs e.g. {{user.age}}.
JSON
{"id":1,"fullName":"Mike","password":"password","email":"mike#email.com","age":38,"gender":"male","created_at":1432736718951,"updated_at":1432736718951}
Do I have to create a helper to extract the attribute value from the model?
function getUserModel(req, res, next) {
User.forge({id: req.user})
.fetch()
.then(function(user){
req.model = user;
return next()
})
.catch(function(err){
debug("Error loading userId[%s]", req.user);
return next(err);
});
}
router.use(ensureAuthenticated)
.use(getUserModel);
router.get('/', function handlePhotoUploadGet(req, res, next) {
var callback = "http://" + req.headers.host + "/cloudinary_cors.html";
var params = {return_delete_token: true, callback: callback,
timestamp: cloudinary.utils.timestamp()};
var dataFormData = cloudinary.utils.sign_request(params);
res.render('photos/upload',
{ title: 'Photo upload',
cloudinary: cloudinary,
dataFormData: JSON.stringify(dataFormData),
user: req.model }
);
});
<input type="text" name="age" aria-label="age" value="{{user.age}}" placeholder="age" class="radius {{#if errors.invalidAttributes.fullName}}error{{/if}}" />
<input type="radio" name="gender" value="male" aria-label="Male" id="male" {{#if_equal user.gender 'male'}}checked{{/if_equal}}><label for="male">Male</label>

In order to answer this question you need to provide the server-side code, to see what you push to response, and client side html as well. Please update your question.
Update:
Change this line:
req.model = user; to this: req.model = user.toJSON();

Related

Cancel a previous axios request on new request in Vue Js

I am trying to implement a search which loads user data from the backend on every word change using #input and populate in datalist, now for every change a new request is generated while the old request is still processing/pending this causes some problems. I am looking to cancel old request on every new request that will take place.
Html code in vue js
<input type="text" class="form-control" placeholder="Search Name" v-model="forms.name" list="getname" #input="inputData()" required> <datalist id="getInput" > <option v-for="option in options">{{option}}</option> </datalist>
Function to load data
axios.get('Url/GetUser/'+ this.forms.name).then((response) => {if(response.data.error){ this.errorNya = response.data; this.loading2 = false;}else{ this.errorNya.username = ""; this.options = response.data; this.loading2= false; this.disableButton = true; }
I have referred the documentation and other sources for this question, and i found a solution which suited me best.
this.cancelFunc();
//Below line creates a cancel token for this request
let axiosSource = axios.CancelToken.source();
this.request = { cancel: axiosSource.cancel };
//then we pass the token to the request we want to cancel.
axios.get('/Url/GetUser/'+ this.forms.username, {cancelToken: axiosSource.token,}).then((response) => {
if (response.data.error) {
this.errorNya = response.data;
this.loading2 = false;
} else {
this.errorNya.username = "";
this.options = response.data;
this.loading2 = false;
this.disableButton = true;
}
},
cancelFunc() {
if (this.request)
this.request.cancel();
},
i know this will not be the best way to do this, but this solved my problem.

vue axios post how to submit formdata array?

I need to submit an ARRAY data because the backend can only recognize such data
Expected effect:
&row[weigh]=0
&row[status]=normal
code:
row:{
weigh: 0,
status: 'normal'
}
actual effect:
row:{
weigh: 0,
status: 'normal'
}
When I submit the data, the console displays JSON instead of Array, but the backend cannot get it
What I need is to be consistent with the results from the form submission below
<form method="POST" >
<input name="row[a]" type="text" value="">
<input name="row[b]" type="text" value="">
public register(rowObject: RowObject): AxiosPromise<any> {
return axios.post('http://localhost/api/register', rowObject);
}
This way you can pass the data in Post method.
rowObject = {
weigh: 0,
status: 'normal'
}
Try this code.
let row = {
weigh: 0,
status: 'normal'
};
let finalArr = [];
Object.keys(row).forEach((key) => {
finalArr.push(`row[${key}]=` + row[key]);
});
console.log(finalArr.join('&'));
// outputs: row[weigh]=0&row[status]=normal
Your code also should pass an array just like this.
data = [
{weigh: 0},
{status: 'normal'}
]
then when you send it to server for example using axios, your code should look like this
axios.post('/api endpoint', {row:data})
.then(response => {
// response here
});
ok
const formData = new FormData()
Object.keys(this.form).forEach(e => {
formData.append(`row[${e}]`, this.form[e])
})

Vue JS fire a method based on another method's output unique ID

I'm trying to render a list of notes and in that list I would like to include the note's user name based on the user_id stored in the note's table. I have something like this, but at the moment it is logging an error stating Cannot read property 'user_id' of undefined, which I get why.
My question is, in Vue how can something like this be executed?
Template:
<div v-for="note in notes">
<h2>{{note.title}}</h2>
<em>{{user.name}}</em>
</div>
Scripts:
methods:{
fetchNotes(id){
return this.$http.get('http://api/notes/' + id )
.then(function(response){
this.notes = response.body;
});
},
fetchUser(id){
return this.$http.get('http://api/user/' + id )
.then(function(response){
this.user = response.body;
});
}
},
created: function(){
this.fetchNotes(this.$route.params.id)
.then( () => {
this.fetchUser(this.note.user_id);
});
}
UPDATE:
I modified my code to look like the below example, and I'm getting better results, but not 100% yet. With this code, it works the first time it renders the view, if I navigate outside this component and then back in, it then fails...same thing if I refresh the page.
The error I am getting is: [Vue warn]: Error in render: "TypeError: Cannot read property 'user_name' of undefined"
Notice the console.log... it the returns the object as expected every time, but as I mentioned if refresh the page or navigate past and then back to this component, I get the error plus the correct log.
Template:
<div v-for="note in notes">
<h2>{{note.title}}</h2>
<em>{{note.user.user_name}}</em>
</div>
Scripts:
methods:{
fetchNotes(id){
return this.$http.get('http://api/notes/' + id )
.then(function(response){
this.notes = response.body;
for( let i = 0; i < response.body.length; i++ ) {
let uId = response.body[i].user_id,
uNote = this.notes[i];
this.$http.get('http://api/users/' + uId)
.then(function(response){
uNote.user = response.body;
console.log(uNote);
});
}
});
},
}
It looks like you're trying to show the username of each note's associated user, while the username comes from a different data source/endpoint than that of the notes.
One way to do that:
Fetch the notes
Fetch the user info based on each note's user ID
Join the two datasets into the notes array that your view is iterating, exposing a user property on each note object in the array.
Example code:
let _notes;
this.fetchNotes()
.then(notes => this.fetchUsers(notes))
.then(notes => _notes = notes)
.then(users => this.joinUserNotes(users, _notes))
.then(result => this.notes = result);
Your view template would look like this:
<div v-for="note in notes">
<h2>{{note.title}}</h2>
<em>{{note.user.name}}</em>
</div>
demo w/axios
UPDATE Based on the code you shared with me, it looks like my original demo code (which uses axios) might've misled you into a bug. The axios library returns the HTTP response in a data field, but the vue-resource library you use returns the HTTP response in a body field. Attempting to copy my demo code without updating to use the correct field would cause the null errors you were seeing.
When I commented that axios made no difference here, I was referring to the logic shown in the example code above, which would apply to either library, given the field names are abstracted in the fetchNotes() and fetchUsers().
Here's the updated demo: demo w/vue-resource.
Specifically, you should update your code as indicated in this snippet:
fetchInvoices(id) {
return this.$http.get('http://localhost/php-api/public/api/invoices/' + id)
// .then(invoices => invoices.data); // DON'T DO THIS!
.then(invoices => invoices.body); // DO THIS: `.data` should be `.body`
},
fetchCustomers(invoices) {
// ...
return Promise.all(
uCustIds.map(id => this.$http.get('http://localhost/php-api/public/api/customers/' + id))
)
// .then(customers => customers.map(customer => customer.data)); // DON'T DO THIS!
.then(customers => customers.map(customer => customer.body)); // DO THIS: `.data` should be `.body`
},
Tony,
Thank you for all your help and effort dude! Ultimately, with the help from someone in the Vue forum, this worked for me. In addition I wanted to learn how to add additional http requests besides the just the user in the fetchNotes method - in this example also the image request. And this works for me.
Template:
<div v-if="notes.length > 0">
<div v-if="loaded === true">
<div v-for="note in notes">
<h2>{{note.title}}</h2>
<em>{{note.user.user_name}}</em>
<img :src="note.image.url" />
</div>
</div>
<div v-else>Something....</div>
</div>
<div v-else>Something....</div>
Script:
name: 'invoices',
data () {
return {
invoices: [],
loaded: false,
}
},
methods: {
fetchNotes: async function (id){
try{
let notes = (await this.$http.get('http://api/notes/' + id )).body
for (let i = 0; notes.length; i++) {
notes[i].user = (await this.$http.get('http://api/user/' + notes[i].user_id)).body
notes[i].image = (await this.$http.get('http://api/image/' + notes[i].image_id)).body
}
this.notes = this.notes.concat(notes)
}catch (error) {
}finally{
this.loaded = true;
}
}

Aurelia Validation validation error detected, but no error message

I have a super simple code I'm trying to validate:
<template>
<form role="form" submit.delegate="submit()" validate.bind="validation">
<div class="form-group">
<label>Test Field</label>
<input type="text" value.bind="testField" class="form-control" validate="Description" placeholder="What needs to be done?" />
<button type="submit">Submit</button>
</div>
</form>
</template>
With the following viewmodel
define(["require", "exports", "../scripts/HttpClient", "aurelia-validation", "aurelia-framework"], function(require, exports, HttpClient) {
var AureliaValidation = require('aurelia-validation').Validation;
var MyViewModel = (function () {
function MyViewModel(httpClient, aureliaValidation, isReadyCallback) {
this.httpClient = httpClient;
var self = this;
self.setupValidation(aureliaValidation);
}
MyViewModel.prototype.activate = function (params, queryString, routeConfig) {
};
MyViewModel.prototype.setupValidation = function (validation) {
this.testField = "";
this.validation = validation.on(this).ensure('testField');
//validation
// .on(this.serviceMetadata.ServiceData[0])
// .ensure('Value');
this.validation = this.validation.notEmpty().maxLength(3);
};
MyViewModel.prototype.submit = function () {
debugger;
if (this.validation.checkAll()) {
//Do Something
}
return null;
};
MyViewModel.inject = [HttpClient, AureliaValidation];
return MyViewModel;
})();
return MyViewModel;
});
Now I got it working for the most part, and the validation is showing false on submit check, the textbox outline color changes etc., however it's not injecting the validation error messages into the DOM. There's no script error message either, how can I troubleshoot this?
Yes, I can see the validation messages in the validationProperties, but they're not written to the UI.
If your browser allows it, find the JSPM packages in the sources and put a breakpoint here, it's the point where the view strategy looks for labels to append error messages to. If you'd have this code in the open, I'd be happy to have a look for you.
Also, what version of aurelia/aurelia-validation are you using?
And finally, did you modify your sample before posting?
`<input value.bind="testField" validate="Description" />`
These two attributes are contradictory. It binds the value to testField, but then you use the validate attribute to explicitly show validation messages for property "Description".

Ajax.ActionLink parameter from DropDownList

I have the following view part:
<div class="editor-label">
#Html.LabelFor(model => model.Type)
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.Type, ElangWeb.Helpers.ModelHelpers.GetExerciseTypes())
</div>
I want to have a link which will generate some partialview based on my model's Type property which is an Enum (I return different partial views based on the type),
I've added the following link:
#Ajax.ActionLink("AddExerciseItem",
"AddExerciseItem",
"Exercise",
new { type=#Model.Type},
new AjaxOptions() { HttpMethod="GET", InsertionMode = InsertionMode.InsertBefore, UpdateTargetId="ExerciseItems"})
My controller action is defined as follows:
public ActionResult AddExerciseItem(ExerciseType type)
{
return PartialView("ExerciseItemOption", new ExerciseItemOption());
}
I however does not work because I have the exeption "Object reference not set to an instance of an object" for my Model. How to resolve this issue?
You could use a normal link:
#Html.ActionLink(
"AddExerciseItem",
"AddExerciseItem",
"Exercise",
null,
new { id = "add" }
)
that you could unobtrusively AJAXify:
// When the DOM is ready
$(function() {
// Subscribe to the click event of the anchor
$('#add').click(function() {
// When the anchor is clicked get the currently
// selected type from the dropdown list.
var type = $('#Type').val();
// and send an AJAX request to the controller action that
// this link is pointing to:
$.ajax({
url: this.href,
type: 'GET',
// and include the type as query string parameter
data: { type: type },
// and make sure that you disable the cache because some
// browsers might cache GET requests
cache: false,
success: function(result) {
// When the AJAX request succeeds prepend the resulting
// markup to the DOM the same way you were doing in your
// AJAX.ActionLink
$('#ExerciseItems').prepend(result);
}
});
return false;
});
});
Now your AddExerciseItem controller action could take the type parameter:
public ActionResult AddExerciseItem(string type)
{
...
}