What is the correct way to save a modified record (EMBER 1.0.0-PRE.4) - ember-data

I have developed a small test application. Loading the person records
works as expected. I can modify firstName and lastName.
In the personedit template I have a action which triggers the save method of
the App.PersonController.
What is the best way to save the modified record (implementation of the save method) ?
JavaScript:
window.App = Ember.Application.create();
DS.RESTAdapter.configure("plurals",{"person" : "people"})
App.Store = DS.Store.extend({
revision: 11,
adapter: DS.RESTAdapter.create({namespace: 'restservice'})
});
var attr = DS.attr;
App.Person = DS.Model.extend({
firstName: attr('string'),
lastName: attr('string'),
birthDay: attr('string')
});
App.Router.map(function(){
this.resource('people',function(){
this.resource('person',{path: 'people/:person_id'});
this.route('edit');
});
});
App.PersonRoute = Ember.Route.extend({
model: function(params){
console.log('PersonRoute: set Model ==> perform App.Person.find(params.person_id)')
var p = App.Person.find(params.person_id);
return p;
},
renderTemplate: function(){
this.render('person');
this.render('personedit',{outlet: 'personedit'});
}
});
App.PersonController = Ember.Controller.extend({
save: function(){
console.log('save: Person:',this.content);
}
});
App.PeopleEditRoute = Ember.Route.extend({
model: function(params){
console.log('EditpersonRoute: set Model ==> perform App.Person.find(params.person_id)')
var p = App.Person.find(params.person_id);
return p;
}
});
Templates:
<script type="text/x-handlebars" data-template-name="application">
<nav>
<div class="navbar">
<div class="navbar-inner">
<ul class="nav">
<li>{{#linkTo "people"}}Personen anzeigen{{/linkTo}}</li>
<li>{{#linkTo "page1"}}Page 1 anzeige{{/linkTo}}</li>
<li>{{#linkTo "page2"}}Page 2 anzeige{{/linkTo}}</li>
</ul>
</div>
</div>
</nav>
<div id="main-content">
{{outlet}}
</div>
</script>
<script type="text/x-handlebars" data-template-name="people">
<div id="people">
<H3>Personen Liste</H3>
<ul>
{{#each person in controller}}
<li>
{{#linkTo "person" person}} {{person.firstName}} {{person.lastName}} {{/linkTo}}
</li>
{{/each}}
</ul>
</div>
<div class="row-fluid">
<div class="span6">
{{outlet}}
</div>
<div class="span6">
{{outlet personedit}}
</div>
</div>
</script>
<script type="text/x-handlebars" data-template-name="person">
<div id="person" class="inline">
<H3>Person View</H3>
<dl>
<dt>Vorname</dt>
<dd>{{content.firstName}}</dd>
<dt>Nachname</dt>
<dd>{{content.lastName}}</dd>
</dl>
</div>
</div>
</script>
<script type="text/x-handlebars" data-template-name="personedit">
<H3>EDITOR</H3>
<fieldset>
<legend>Person</legend>
<label>Vorname</label>
{{view Ember.TextField valueBinding="content.firstName"}}
<span class="help-block">Example block-level help text here.</span>
<label>Nachname</label>
{{view Ember.TextField valueBinding="content.lastName"}}
<button {{action "save" }} class="btn">Save</button>
</fieldset>
</script>

I have found the correct way of saving in another answer. The implementation of my save method needs only one additional line:
App.PersonController = Ember.Controller.extend({
save: function(){
console.log('save: Person:',this.content);
this.content.get('transaction').commit();
}

Related

Bind class item in the loop

i want to bind my button only on the element that i added to the cart, it's working well when i'm not in a loop but in a loop anything happen. i'm not sure if it was the right way to add the index like that in order to bind only the item clicked, if i don't put the index every button on the loop are binded and that's not what i want in my case.
:loading="isLoading[index]"
here the vue :
<div class="container column is-9">
<div class="section">
<div class="columns is-multiline">
<div class="column is-3" v-for="(product, index) in computedProducts">
<div class="card">
<div class="card-image">
<figure class="image is-4by3">
<img src="" alt="Placeholder image">
</figure>
</div>
<div class="card-content">
<div class="content">
<div class="media-content">
<p class="title is-4">{{product.name}}</p>
<p class="subtitle is-6">Description</p>
<p>{{product.price}}</p>
</div>
</div>
<div class="content">
<b-button class="is-primary" #click="addToCart(product)" :loading="isLoading[index]"><i class="fas fa-shopping-cart"></i> Ajouter au panier</b-button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
here the data :
data () {
return {
products : [],
isLoading: false,
}
},
here my add to cart method where i change the state of isLoading :
addToCart(product) {
this.isLoading = true
axios.post('cart/add-to-cart/', {
data: product,
}).then(r => {
this.isLoading = false
}).catch(e => {
this.isLoading = false
});
}
You can change your isLoading to an array of booleans, and your addToCart method to also have an index argument.
Data:
return {
// ...
isLoading: []
}
Methods:
addToCart(product, index) {
// ...
}
And on your button, also include index:
#click="addToCart(product, index)"
By changing isLoading to an empty array, I don't think isLoading[index] = true will be reactive since index on isLoading doesn't exist yet. So you would use Vue.set in your addToCart(product, index) such as:
this.$set(this.isLoading, index, true)
This will ensure that changes being made to isLoading will be reactive.
Hope this works for you.
add on data productsLoading: []
on add to cart click, add loop index to productsLoading.
this.productsLoading.push(index)
after http request done, remove index from productsLoading.
this.productsLoading.splice(this.productoading.indexOf(index), 1)
and check button with :loading=productsLoading.includes(index)
You can create another component only for product card,
for better option as show below
Kindly follow this steps.
place the content of card in another vue component as shown below.
<!-- Product.vue -->
<template>
<div class="card">
<div class="card-image">
<figure class="image is-4by3">
<img src="" alt="Placeholder image">
</figure>
</div>
<div class="card-content">
<div class="content">
<div class="media-content">
<p class="title is-4">{{product.name}}</p>
<p class="subtitle is-6">Description</p>
<p>{{product.price}}</p>
</div>
</div>
<div class="content">
<b-button class="is-primary" #click="addToCart(product)" :loading="isLoading"><i class="fas fa-shopping-cart"></i> Ajouter au panier</b-button>
</div>
</div>
</div>
</templete>
<script>
export default {
name: "Product",
data() {
return {
isLoading: false
}
},
props: {
product: {
type: Object,
required: true
}
},
methods: {
addToCart(product) {
this.isLoading = true
axios.post('cart/add-to-cart/', {
data: product,
}).then(r => {
this.isLoading = false
}).catch(e => {
this.isLoading = false
});
}
}
}
</script>
Change your component content as shown below.
<template>
<div class="container column is-9">
<div class="section">
<div class="columns is-multiline">
<div class="column is-3" v-for="(product, index) in computedProducts">
<product :product="product" />
</div>
</div>
</div>
</div>
</templete>
<script>
import Product from 'path to above component'
export default {
components: {
Product
}
}
</script>
so in the above method you can reuse the component in other components as well.
Happy coding :-)

Getting and Display Records from API

I am trying to get list of tracks related to an album and i am having problem displaying the records using my API. How can i get to display all the tracks under an album and it will give me the Track ID, Track Title etc.
<ul class="tracklist">
<li class='tracklistRow' v-for="album in albums">
<div class='trackCount'>
<img class='play' src='/assets/images/icons/play-white.png' onclick='playSong()'>
<img class='pause' src='/assets/images/icons/pause.png' style='display:none;' onclick='pauseSong()'>
<span class='trackNumber'>{{ album.track.id }}</span>
</div>
<div class='trackInfo'>
<span class='trackName'>{{ album.track.track.title }}</span>
</div>
<div class='trackOptions'>
<img class='optionsButton' src='/assets/images/icons/more.png'>
</div>
<div class='trackDuration'>
<span class='duration'>{{ album.track.track.duration }}</span>
</div>
</li>
<div class="rLabel" v-if="album">
© {{ album.albumDetails.recordlabel }}
</div>
</ul>
<script>
import axios from 'axios';
export default {
name: 'album',
props:['id'],
data: function(){
return {
album: null,
albums: []
}
},
methods: {
getData(){
var that = this
axios.get('http://api.localhost/api/album/'+this.id)
.then(function (response){
that.album = response.data.data;
})
}
},
mounted: function(){
this.getData()
}
}
</script>

Vue.js how to use radio buttons inside v-for loop

I'm trying to use radio buttons, so that users can choose one of the photos as their profile photo:
<ul v-for="p in myPhotos">
<li>
<div>
<div>
<div>
photo id: {{p.imgId}}
</div>
<input type="radio" v-model="profileImg" value="p.imgId"> Choose as profile image
</div>
<div><img v-bind:src="BASE_URL +'/uploads/' + userId + '/'+ p.imgId" /> </div>
</div>
</li>
</ul>
The values are otained like this:
created () {
axios.get(this.BASE_URL + '/profile/g/myphotos/', this.jwt)
.then( res => {
this.myPhotos = res.data.photos;
this.showUploadedPhoto = true;
this.profileImg = res.data.profileImg
})
.catch( error => {
console.log(error);
})
},
When an photo is chosen, the profileImg variable should be set to that photo's imgId.
The problem is how to let users to choose only one photo as profile image inside this v-for loop?
Update: a photo my myPhotos object that I'm iterate over:
By providing name you can treat it as one element
var app = new Vue({
el:"#app",
data:{
images:[{imgId:1},{imgId:2},{imgId:3}],
profileImg:2
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div>
<div v-for="image in images">
<input type="radio" v-model="profileImg" name="profileImg" :value="image.imgId"> Choose as profile image
</div>
You have selected : {{profileImg}}
</div>
</div>
How about setting name attribute for the input field
<ul v-for="p in myPhotos">
<li>
<div>
<div>
<div>
photo id: {{p.imgId}}
</div>
<input type="radio" name="user_profile_photo" v-model="profileImg" :value="p.imgId"> Choose as profile image
</div>
<div><img v-bind:src="BASE_URL +'/uploads/' + userId + '/'+ p.imgId" /> </div>
</div>
</li>
</ul>

Vue add a component on button click

I have three templates. AddCard.vue , ImageCard.vue and VideoCard.vue
AddCard.vue has two buttons on it one is to add the Image Card and Other To Add the video Card.. I need to add components based on the button click. Here are three templates and my index.html file.
index.html
<!doctype html>
<html lang="en">
<head>
</head>
<body>
<div id="app">
<div class="container">
<div id="dynamic_components">
<!-- add components here -->
</div>
<addcard></addcard>
</div>
</div>
<script src="{{ asset('js/app.js') }}"></script>
</body>
</html>
AddCard.vue
<template>
<div class="buttons">
ul class="no-margin-list">
<li #click="imagecard">
<span class="card_icon">
<img :src="'img/image.jpg'" >
</span>
<p>Add Image Card</p>
</a>
</li>
<li #click="videocard">
<span class="card_icon">
<img :src="'img/video.jpg'" >
</span>
<p>Add Video Card</p>
</a>
</li>
</div>
</template>
<script>
export default {
computed: {
},
methods: {
imagecard(val) {
//how to add image card
},
videocard() {
//how to add video card
}
},
}
</script>
ImageCard.vue
<template>
<h1> I am a image Card </h1>
</template>
<script>
</script>
VideoCard.vue
<template>
<h1> I am a Video Card </h1>
</template>
<script>
</script>
I need to add components dynamically one after another in the <div id="dynamic_components"> . User can add as many as cards they want.
How do I add the components dynamically. Please point me to a tutorial.
Uses v-for + dynamic component.
Vue.config.productionTip = false
Vue.component('card1', {
template: '<div>Card:<span style="background-color:green">{{title}}</span></div>',
props: ['title']
})
Vue.component('card2', {
template: '<div>Card:<span style="background-color:blue">{{title}}</span></div>',
props: ['title']
})
Vue.component('card3', {
template: '<div>Card:<span style="background-color:yellow">{{title}}</span></div>',
props: ['title']
})
new Vue({
el: '#app',
data() {
return {
cards: [
{'card': {'title': 'I am one card1'}, 'card-type':'card1'},
{'card': {'title': 'I am one card2'}, 'card-type':'card2'}
]
}
},
computed: {
computedNoCard1: function () {
let availableCards = new Set(['card2', 'card3'])
return this.cards.filter((item) => {
return availableCards.has(item['card-type'])
})
}
},
methods: {
addCard: function () {
let supportedCards = ['card1', 'card2', 'card3']
let seed = Math.floor(Math.random()*supportedCards.length)
this.cards.push({'card': {'title': 'I am new card for ' + supportedCards[seed]}, 'card-type': supportedCards[seed]})
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app">
<button #click="addCard()">Add Card</button>
<table>
<tr><th>Data Property</th><th>Computed Property</th></tr>
<tr>
<td>
<div v-for="(card, index) in cards" :key="index">
<component :is="card['card-type']" :title="card.card.title">
</component>
</div>
</td>
<td>
<div v-for="(card, index) in computedNoCard1" :key="index">
<component :is="card['card-type']" :title="card.card.title">
</component>
</div>
</td>
</tr>
</table>
</div>

Aurelia -- Route Change on Form Submission Issue

Aurelia newbie here and I have hit a wall.
So, this code works just fine and the route change happens, but it only happens after the Submit button on the home.html file is clicked TWICE. On the first Submit button click, I get the following error: ERROR [app-router] Error: Route not found: /anonymous-wow-armory-profile/.
My question is why does it work after two form submissions, but not the first one? I know I am missing something in the process here.
home.html
<template>
<div class="container-fluid">
<div class="row">
<div class="col-md-12 nav-home text-center">
Create Profile
Bug Report
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="logo">
<img src="dist/assets/images/logo.png" alt="Logo" />
</div>
</div>
</div>
<div class="row row-bottom-pad">
<div class="col-md-4"></div>
<div class="col-md-4">
<div class="profile-creation-box">
<div class="box-padding">
<strong>Masked Armory</strong> is the most well known anonymous World of Warcraft (WoW) profile source in the Real Money Trading (RMT) market. We take everything to the next level with offering alternate gear sets, sorted reputation display, Feat of Strength / Legacy achievement display, and much more!<br /><br />
Come make a profile at Masked Armory today and see that we are the best solution for all of your anonymous WoW Armory profile needs!
</div>
</div>
</div>
<div class="col-md-4"></div>
</div>
<div class="row">
<div class="col-md-4"></div>
<div class="col-md-4 container-bottom-pad">
<div class="profile-creation-box">
<div class="box-padding">
<form class="form-horizontal" role="form" submit.delegate="submit()">
<div class="form-group">
<label class="col-sm-3 control-label">Region</label>
<div class="col-sm-9">
<label class="radio-inline">
<input type="radio" name="region_name" value="us" checked.bind="postData.region"> United States
</label>
<label class="radio-inline">
<input type="radio" name="region_name" value="eu" checked.bind="postData.region"> Europe
</label>
</div>
</div>
<div class="form-group">
<label for="server_name" class="col-sm-3 control-label">Server</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="server_name" placeholder="Server Name" value.bind="postData.serverName">
</div>
</div>
<div class="form-group">
<label for="character_name" class="col-sm-3 control-label">Character</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="character_name" name="character_name" placeholder="Character Name" value.bind="postData.characterName">
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-3 col-sm-9">
<div class="checkbox">
<label>
<input type="checkbox" id="altgear" name="altgear"> Add Alternate Gearset
</label>
</div>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-3 col-sm-9">
<button type="submit" class="btn btn-danger">Create Armory Profile</button>
</div>
</div>
</form>
</div>
</div>
</div>
<div class="col-md-4"></div>
</div>
</div>
</template>
home.js
import {inject} from 'aurelia-framework';
import {HttpClient} from 'aurelia-http-client';
import {Router} from 'aurelia-router';
#inject(Router)
export class Home {
postData: Object = {};
data: string = '';
code: string = '';
loading: boolean = false;
http: HttpClient = null;
apiUrl: string = 'http://localhost:8000/api/v1';
constructor(router) {
this.http = new HttpClient().configure(x => {
x.withBaseUrl(this.apiUrl);
x.withHeader('Content-Type', 'application/json');
});
this.maRouter = router;
}
submit() {
console.log(this.postData);
this.http.post('/armory', JSON.stringify(this.postData)).then(response => {
this.data = response.content;
this.code = response.statusCode.toString();
this.loading = false;
});
this.maRouter.navigateToRoute('armory', {id: this.data});
}
}
armory.js
import {inject} from 'aurelia-framework';
import {HttpClient} from 'aurelia-http-client';
export class Armory {
postData: Object = {};
data: string = '';
code: string = '';
loading: boolean = false;
http: HttpClient = null;
apiUrl: string = 'http://localhost:8000/api/v1';
profileId: number = 0;
constructor() {
this.loading = true;
this.http = new HttpClient().configure(x => {
x.withBaseUrl(this.apiUrl);
x.withHeader('Content-Type', 'application/json');
});
}
activate(params, routeConfig) {
this.profileId = params.id;
this.getArmoryData();
}
getArmoryData() {
return this.http.get("/armory/" + this.profileId).then(response => {
this.data = response.content;
console.log(this.data);
this.code = response.statusCode.toString();
this.loading = false;
});
}
}
What am I missing here?
Thanks for your help!
Please, provide your router configuration
Anyway I see some issues already. You try to navigate when this.data is not set, just wait for response:
this.http.post('/armory', JSON.stringify(this.postData)).then(response => {
this.data = response.content;
this.code = response.statusCode.toString();
this.loading = false;
this.maRouter.navigateToRoute('armory', {id: this.data});
});
and we do activate page only if this.getArmoryData() succeed here (if needed), also canActivate() maybe used too
activate(params, routeConfig) {
this.profileId = params.id;
return this.getArmoryData();
}
also would be better to set this.loading = true;, inside armory .activate() and in home.js in submit() before sending data