Object disappearing from the view on save in Ember.js - javascript

Essentially I have a list of objects, and the user is able to add new ones by typing into a textbox. As the user types, the new object appears. However, when the user clicks save, the object disappears from the listing. It is successfully saved to the database (Ruby on Rails/SQLite) so it appears on a full refresh. Why is it doing this, and how can I get it to stay on the screen when the user presses submit?
Here is some relevant code:
index.hbs
<div class="messages">
{{#each userhashtag in controller}}
{{render "userhashtag" userhashtag}}
{{/each}}
{{#link-to 'userhashtags.new'}} Add Hashtag{{/link-to}}
</div>
{{outlet}}
show.hbs
{{userhashtag.name}} <button class="btn btn-dancer" type="submit" {{action "destroy"}}>Remove</button><br>
new.hbs
<form role="form">
<fieldset>
<div class="form-group">
{{view Ember.TextField valueBinding='name' class='form-control' name='name' viewName='nameField'}}
</div>
<div class="btn-group">
<button type="submit" {{action "save"}} class="btn btn-success">Submit</button>
</div>
route
App.UserhashtagsNewRoute = Ember.Route.extend({
model: function(){
return this.store.createRecord('userhashtag');
},
setupController: function(controller, model){
controller.set('model', model);
},
renderTemplate: function() {
this.render('app/templates/userhashtags/new');
},
actions: {
save: function(){
return this.controller.get('model').save().then(function(){
this.transitionTo('userhashtags');
}.bind(this));
}
}
});

Make sure that you are manually pushing the new model into your Array collection (not sure how you're managing the collection). I'd suggest moving the #save action into a controller where you manage the list of objects.
// assuming "list" is some reference to your collection list within your controller
save: function() {
this.get('userhashtag').save().then(function(_model){
list.pushObject(_model);

Related

Form modal binding in laravel with vue js

I have 2 models Tour.php
public function Itinerary()
{
return $this->hasMany('App\Itinerary', 'tour_id');
}
and Itinerary.php
public function tour()
{
return $this->belongsTo('App\Tour', 'tour_id');
}
tours table:
id|title|content
itineraries table:
id|tour_id|day|itinerary
In tour-edit.blade.php view I have used vue js to create or add and remove input field for day and plan dynamically.
Code in tour-create.blade.php
<div class="row input-margin" id="repeat">
<div class="col-md-12">
<div class="row" v-for="row in rows">
<div class="row">
<div class="col-md-2">
<label >Day:</label>
<input type="text" name="day[]"
class="form-control">
</div>
<div class="col-md-8">
{{ Form::label('itinerary', " Tour itinerary:", ['class' => 'form-label-margin'])}}
{{ Form::textarea('itinerary[]',null, ['class' => 'form-control','id' => 'itinerary']) }}
</div>
<div class="col-md-2">
<button class="btn btn-danger" #click.prevent="deleteOption(row)">
<i class="fa fa-trash"></i></button>
</div>
</div>
</div>
<div class="row">
<button class="btn btn-primary add" #click.prevent="addNewOption" >
<i class="fa fa-plus"></i> Add Field</button>
</div>
</div>
</div>
I want to populate these fields with their respective data. But all data i.e itinerary belonging to a tour are being displayed in itinerary textbox in JSON format.
My vue js sript is:
<script>
var App = new Vue({
el: '#repeat',
data: {
day:1 ,
rows:[
#foreach ($tour->itinerary as $element)
{day: '{{$element->day}}', plan: '{{$element->plan}}'},
#endforeach
]
},
methods: {
addNewOption: function() {
var self = this;
self.rows.push({"day": "","itinerary":""});
},
deleteOption: function(row) {
var self = this;
self.rows.splice(row,1);
},
}
});
</script>
I would avoid mixing blade into JavaScript, instead the best option is to make an ajax call to an api route which returns your data in json, which can then be processed by Vue:
methods:{
getItinerary(){
axios.get('api/itinerary').then(response => {
this.itinerary = response.data;
})
}
}
However, with this approach you will likely need to use vue-router rather than laravel web routes, which puts us into SPA territory.
If that's not an option (i.e. you still want to use blade templates), you should take a look at this answer I gave the other day which shows you how to init data from your blade templates.
What you seem to be doing is using laravel's form model binding to populate your forms, not Vue, so your model data is not bound to the view. So, you will need to decide which one you want to use. If it's vue you just want to use a normal form and bind the underlying data to it using v-model:
Now any updates in the view will automatically be updated by Vue. I've put together a JSFiddle that assumes you will want to continue using Laravel web routes and blade templates to show you one approach to this problem: https://jsfiddle.net/w6qhLtnh/

Getting proper data model when rendering into outlet in Ember

I am trying to create a select box in a modal that has a list of all cars in inventory. If I enter the app from a page that has all the data loaded and open the modal it works correctly. However if I am on a route that doesn't have the data loaded, then open the modal the select options do not show. Furthermore the options will never update from that point. How do I get the proper data to load? Should not this work since I am fetching the data direct from the store?
Dms.SellDialogController = Ember.ArrayController.extend({
cars: function() {
return this.store.find('car').filterProperty('isSold', false);
}.property('model'),
selectedCar: '',
carObjects: function() {
var cars = this.get('cars').map(function(car) {
return obj = {id: car.get('id'), key: car.get('keyNumber'), label: 'KEY#:'+car.get('keyNumber') + ' - STOCK#:' + car.get('stock')+' '+car.get('year')+' '+car.get('vModel')};
});
cars.sort(function(a, b){return a.key-b.key;})
return cars;
}.property('route'),
title: 'Select the car you are selling'
});
ApplicationRoute...
...
openModal: function(modalName, model) {
this.render(modalName, {
into: 'application',
outlet: 'modal',
model: model
});
},
Action to open modal inside application template
{{action 'openModal' 'sellDialog' model}}
and the templates
<script type="text/x-handlebars" id="components/modal-dialog">
<div class='modal-overlay' {{action "closeModal" target=cntrllr}}>
<div class='modal' {{action "dngn" target=cntrllr bubbles=false}}>
<div class='modal-content'>
<div class='modal-header'>
<button type="button" class="close" aria-label="Close" {{action "closeModal" target=cntrllr}}><span aria-hidden="true">×</span></button>
<h4 class="modal-title">{{title}}</h4>
</div>
{{yield}}
</div>
</div>
</div>
</script>
<script type="text/x-handlebars" data-template-name="sellDialog">
{{#modal-dialog title=title cntrllr=controller model=model}}
<div class="modal-body">
<span class='pull-left'>Key Number:
{{view "select" content=carObjects optionValuePath="content.id" optionLabelPath="content.label" selection=selectedCar}}
</span>
</div>
<div class="modal-footer">
<button {{action "modalAction" controller "sell" selectedCar.id}}>Sell</button>
<button {{action "closeModal"}}>Cancel</button>
</div>
{{/modal-dialog}}
</script>
UPDATE 2015/5/4:
I have a hack to make it work for now. I added a cars property in the application route and I am getting it in SellDialogController. This looks like a bug to me. I will make a jsFiddle when I get some time and check it further before issuing a bug report.
try using setupController on the Application Route, since setupController is always called.
import Ember from 'ember';
export default Ember.Route.extend({
model: Ember.RSVP.hash({
optionsForSelect: function() {
// fetch your select data here
},
otherData: function() {
// get your regular model data here
}
}),
setupController: function(controller, model) {
this.controllerFor('sellDialog').set('model', model.optionsForSelect);
this.controller.set('model', model.otherData);
}
});

Calling code after {{#each}} has rendered in ember.js?

I am trying to call some code after an Ember {{#each}} tag has finished looping through its items. I have seen other questions that looked similar and the answer always implemented didInsertElement on the view. This does not seem to work for me as I am trying to access html objects that are not rendered with the view because they are in the {{#each}}.
Here is what my html looks like.
<script type="text/x-handlebars" id="user">
{{#if isEditing}}
<div class="well">
{{partial 'user/edit'}}
<button {{action 'doneEditing'}} class="btn btn-default">Save</button>
<button {{action 'cancelEditing'}} class="btn btn-default">Cancel</button>
</div>
{{else}}
<button {{action 'edit'}} class="btn btn-default">Edit</button>
{{/if}}
</script>
<script type="text/x-handlebars" id="user/edit">
{{#view 'editor'}}
<div id="sList" class="btn-group-vertical" role="group">
{{#each s in model}}
<button class="btn btn-default">
{{s.theme}}
</button>
{{/each}}
</div>
{{/view}}
</script>
And my javascript
App.UserRoute = Ember.Route.extend({
model: function(params) {
return this.store.all('strength')
}
});
App.UserController = Ember.ObjectController.extend({
isEditing: false,
actions: {
edit: function(){
this.set("isEditing", true);
},
doneEditing: function(){
this.set("isEditing", false);
},
cancelEditing: function(){
this.set("isEditing", false);
}
}
});
App.EditorView = Ember.View.extend({
didInsertElement: function() {
//Do something to the button elements
}
});
When I try to run this, as soon as I hit the edit button and the partial loads, I get an error in the console after didInsertElement tried to access the button elements. It as if the elements in the div have not rendered yet. So how can I tell if the {{#each}} is done inserting elements into the html? I know this may be confusing but any and all help is appreciated.
Schedule your code in the afterRender queue to run after the contents of the view has been rendered.
App.EditorView = Ember.View.extend({
didInsertElement: function() {
Ember.run.schedule('afterRender', function() {
this.$().find('button').each(function() {
// $(this) is a button in this function
});
}.bind(this));
}
});

Backbone bind Model to View

I'm currently learning how to deal with backbone.js.
Currently I have the problem that the model isnt updating when the view does through user input.
When I change the input of the textarea and I click the button, I send the old data since the view didnt notify the model that a change happened.
What do I do wrong?
PostView
define([
'jquery',
'underscore',
'backbone',
'postModel'
], function($, _, Backbone, PostModel){
var PostView = Backbone.View.extend({
timer: null,
el: $('.admin__wrapper'),
template: $('#post_template'),
initialize: function () {
console.log('Initializing Post View');
this.model = new PostModel({id: this.id});
this.listenTo(this.model, 'change', this.render);
this.model.fetch();
},
events: {
'keyup textarea': 'keyup',
'click button': 'submit'
},
submit: function(event) {
this.model.save();
},
render: function(){
var template = _.template(this.template.html(), { post: this.model });
this.$el.html(template);
return this;
}
});
return PostView;
});
PostTemplate
<script type="text/template" id="post_template">
<div class="post__title">
<div class="entry">
<input class="input--less" name="title" type="text" value="<%= post.get('title') %>" placeholder="Post title">
</div>
</div>
<div class="entry post__panel post__markdown">
<header class="entry__header post__panel__header">
<span><small>markdown</small></span>
</header>
<div class="post__boundary">
<textarea name="content" id="editor" class="input--less post__editor" rows="5"><%= post.get('content') %></textarea>
</div>
</div>
<div class="entry post__panel post__html">
<header class="entry__header post__panel__header">
<span><small>preview</small></span>
<span id="reading-time" class="float--right" style="text-align: right"></span>
</header>
<div class="post__boundary">
<div class="post__preview" id="preview"><%= post.get('content_compiled') %></div>
</div>
</div>
<div class="post__footer">
<span class="batch--tag-4 post__icon"></span>
<button class="button button--blue post__button">Publish</button>
</div>
</script>
When the user clicks submit your not getting the data from the view you are simply saving the model with defaults, therefor not firing any change events, you will have to do something like this in keyup method which is triggered by the keyup event.
keyup: function(e)
{
this.model.set(e.target.name, e.target.value, {silent: true});
}
Assuming you correctly named your textarea, the correct model attribute will be updated. So now when this.model.save is called by submit these attributes will be saved triggering the change event and re-rendering the view.
When you change the form in your page, this change is not automatically set in your model, you have to set it by yourself.
Backbone does not provide 2 way data binding like Angular Js, you need to set the model yourself and then sync the model.
Try Backbone Mutators for 2 way data binding in Bakcbone.
Also as a follow up read this article

populating select box using emberjs select view

I working on a toy app to learn ember. I have recipes that belong to styles. A style just consists of a name attribute (and the implicit id as well). I am having trouble setting up a select field in the new template to select which style the recipe is.
Here's what I currently have for my routes, controllers and templates:
Routes:
App.RecipesNewRoute = Ember.Route.extend({
model: function() {
return App.Recipe.createRecord({
title: '',
description: '',
instructions: ''
});
},
setupController: function(controller, model) {
controller.set('content', model);
}
});
Controllers:
App.RecipesController = Ember.ArrayController.extend({});
App.RecipesNewController = Ember.ObjectController.extend({
create: function() {
this.transitionToRoute('recipes.show', this.content);
},
cancel: function() {
this.content.deleteRecord();
this.transitionToRoute('recipes.index');
},
buttonTitle: 'Add Beer Recipe'
});
Templates:
recipes/new and form partial:
<script type="text/x-handlebars" data-template-name="recipes/new">
{{ partial 'recipes/form' }}
</script>
<script type="text/x-handlebars" data-template-name="recipes/_form">
{{ title }}
<form>
<fieldset>
<div>
<label {{bindAttr for="titleField.elementId"}}>Title</label>
{{view Ember.TextField valueBinding='title' name='title' viewName='titleField'}}
</div>
<div>
<label>Style</label>
{{view Ember.Select valueBinding='style' name='style' viewName='styleField' contentBinding='App.Style.find()'}}
</div>
<div>
<label {{bindAttr for='descriptionField.elementId'}}>Description</label>
{{view Ember.TextArea valueBinding='description' name='description' viewName='descriptionField'}}
</div>
<div>
<label {{bindAttr for='instructionsField.elementId'}}>Instructions</label>
{{view Ember.TextArea valueBinding='instructions' name='instructions' viewName='instructionsField'}}
</div>
<a href='#' {{action create target='controller'}}>{{buttonTitle}}</a>
</fieldset>
</form>
<a href='#' {{action cancel target='controller'}}>Cancel</a>
</script>
The main takeaway being: {{view Ember.Select valueBinding='style' name='style' viewName='styleField' contentBinding='App.Style.find()'}}
I've also tried setting up a styles variable in the controller:
In RecipesNewController: styles: App.Style.find() and in the view {{view Ember.Select valueBinding='style' name='style' viewName='styleField' contentBinding='styles' optionValuePath='styles.id' optionLabelPath='styles.name'}}
as well as in the route's setupController: controller.set('styles', App.Style.find()) (with the same view code as with setting it in the controller).
Any help would be great, I'm sure it's something simple.
Thanks
Update
So I think I've almost got it.
In my RecipesNewRoute's setupController I have controller.set('styles', App.Style.find());. Then in my view I have {{view Ember.Select valueBinding='style' name='style' viewName='styleField' contentBinding='controller.styles' contentValuePath='content.id' contentLabelPath='content.name'}}. Now my problem is I have the select box being populated, except the label and value are set to <App.Style:ember407:100> instead of the id and the name.
The Ember Select uses optionValuePath and optionLabelPath, and it appears you are using contentValuePath and contentLabelPath.
An example jsFiddle here: http://jsfiddle.net/nrionfx/BJwLR/2/
From the ember docs: http://emberjs.com/api/classes/Ember.Select.html
{{view Ember.Select
contentBinding="App.programmers"
optionValuePath="content.id"
optionLabelPath="content.firstName"}}

Categories

Resources