As a marionnette beginner, I am trying to make a simple chat application using Collection and CollectionViews.
My collection won't have a fetch method since the messages only come from a particular event.
In the piece of code below my click event is not catched and I wonder why.
Should the 'send message' event be handled by the Collection view ?
Do I need to call App.chat.show(MsgListView) to display the messages ?
TBox.module("ChatApp", function(ChatApp, App, Backbone, Marionette, $, _) {
App.addRegions({
chat: "#chat-messages",
});
// Models
// ------
MsgEntry = Backbone.Model.extend({});
// Collections
// -----------
MsgCollection = Backbone.Collection.extend({
model: MsgEntry
})
// VIews
// -----
MsgView = Backbone.Marionette.ItemView.extend({
template: '#chat-entry-template',
});
MsgListView = Backbone.Marionette.CollectionView.extend({
itemView: MsgView,
events: {
"click #chat-send-btn": "handleNewMessage"
},
handleNewMessage: function(data) {
console.log("CLICK" + data);
},
});
// Init & Finalize
// ---------------
ChatApp.addInitializer(function() {
var msgCollection = new MsgCollection({});
var msgEntry = new MsgEntry({'msg': 'Hello World'});
msgCollection.add(msgEntry);
var msgListView = new MsgListView({collection: msgCollection});
});
});
HTML template
<body>
<!-- templates -->
<script type="text/template" id="status-view-template">
<div>Connecting ...</div>
</script>
<script type="text/template" id="chat-entry-template">
Hello <%= msg =>
</script>
<div id="app">
<div id="sidebar">
<div id="chat">
<h3>Chat</h3>
<div id="chat-messages">
</div>
<div id-"chat-input">
<input type="text" name="msg" />
<button id="chat-send-btn">Send</button>
</div>
</div>
</div>
<!-- main -->
<div id="page">
</div>
<div>
</body>
Ok I made it work with App.chat.show(msgListView);
Also the events hash only takes car of the ItemView events, not other dom events.
// Init & Finalize
// ---------------
ChatApp.addInitializer(function() {
App.vent.trigger("app:started", "ChatApp");
var msgCollection = new MsgCollection([{foo :'bar', foo: 'lol'}]);
var msgListView = new MsgListView({collection: msgCollection});
// render and display the view
App.chat.show(msgListView);
});
Related
I want to make tab switcher auto decide the slot for the switcher but when I am trying to make it dynamic with the help of observable no data is showing the tab content area until I write the slot area statically. With observable variable, the slot is not getting the selected Slot value.
Please check how I can do this.
slot = [[selectedSlot]] //using for the slot value in html
this.selectedSlot = ko.observable('settings');
<div id="tabbardemo">
<oj-dialog class="tab-dialog hidden" id="tabDialog" dialog-title="Tab data">
<div slot="body">
<oj-form-layout>
<oj-input-text id="t1" value="{{newTabTitle}}" label-hint="Title"></oj-input-text>
</oj-form-layout>
</div>
<div slot="footer">
<oj-button id="idOK" on-oj-action="[[addTab]]">OK</oj-button>
<oj-button id="idCancel" on-oj-action="[[closeDialog]]">Cancel</oj-button>
</div>
</oj-dialog>
<oj-button id="addTab" on-oj-action="[[openDialog]]">Add Tab</oj-button>
<br/>
<br/>
<oj-tab-bar contextmenu="tabmenu" id="hnavlist" selection="{{selectedItem}}" current-item="{{currentItem}}" edge="top" data="[[dataProvider]]"
on-oj-remove="[[onRemove]]">
<template slot="itemTemplate" data-oj-as="item">
<li class="oj-removable" :class="[[{'oj-disabled' : item.data.disabled}]]">
<a href="#">
<oj-bind-text value="[[item.data.name]]"></oj-bind-text>
</a>
</li>
</template>
<oj-menu slot="contextMenu" class="hidden" aria-label="Actions">
<oj-option data-oj-command="oj-tabbar-remove">
Removable
</oj-option>
</oj-menu>
</oj-tab-bar>
<oj-switcher value="[[selectedItem]]">
<div slot="[[selectedSlot]]"
id="home-tab-panel"
role="tabpanel"
aria-labelledby="home-tab">
<div class="demo-tab-content-style">
<h2>Home page content area</h2>
</div>
</div>
<div slot="tools"
id="tools-tab-panel"
role="tabpanel"
aria-labelledby="tools-tab">
<div class="demo-tab-content-style">
<h1>Tools Area</h1>
</div>
</div>
<div slot="base"
id="base-tab-panel"
role="tabpanel"
aria-labelledby="ba`enter code here`se-tab">
<div class="demo-tab-content-style">
<h1>Base Tab</h1>
</div>
</div>
</oj-switcher>
<br>
<div>
<p class="bold">Last selected list item:
<span id="results">
<oj-bind-text value="[[selectedItem]]"></oj-bind-text>
</span>
</p>
</div>
</div>
JS code below
require(['ojs/ojcontext',
'knockout',
'ojs/ojbootstrap',
'ojs/ojarraydataprovider',
'ojs/ojknockout',
'ojs/ojnavigationlist',
'ojs/ojconveyorbelt',
'ojs/ojdialog',
'ojs/ojbutton',
'ojs/ojinputtext',
'ojs/ojformlayout',
'ojs/ojswitcher',
],
function (Context, ko, Bootstrap, ArrayDataProvider) { // this callback gets executed when all required modules are loaded
function ViewModel() {
this.data = ko.observableArray([{
name: 'Settings',
id: 'settings'
},
{
name: 'Tools',
id: 'tools'
},
{
name: 'Base',
id: 'base'
}
]);
this.selectedSlot = ko.observable('settings'); //Sepecifically mentioned to show what it is the objective
this.dataProvider = new ArrayDataProvider(this.data, { keyAttributes: 'id' });
this.selectedItem = ko.observable('settings');
this.currentItem = ko.observable();
this.tabCount = 0;
this.newTabTitle = ko.observable();
this.delete = (function (id) {
var hnavlist = document.getElementById('hnavlist');
var items = this.data();
for (var i = 0; i < items.length; i++) {
if (items[i].id === id) {
this.data.splice(i, 1);
Context.getContext(hnavlist)
.getBusyContext()
.whenReady()
.then(function () {
hnavlist.focus();
});
break;
}
}
}).bind(this);
this.onRemove = (function (event) {
this.delete(event.detail.key);
event.preventDefault();
event.stopPropagation();
}).bind(this);
this.openDialog = (function () {
this.tabCount += 1;
this.newTabTitle('Tab ' + this.tabCount);
document.getElementById('tabDialog').open();
}).bind(this);
this.closeDialog = function () {
document.getElementById('tabDialog').close();
};
this.addTab = (function () {
var title = this.newTabTitle();
var tabid = 'tid' + this.tabCount;
this.data.push({
name: title,
id: tabid
});
this.closeDialog();
}).bind(this);
}
Bootstrap.whenDocumentReady().then(function () {
ko.applyBindings(new ViewModel(), document.getElementById('tabbardemo'));
});
}
);
It is a bit complex to understand when you copy from JET cookbook. You have done almost everything right. Just make the following changes:
1) Remove this:
Bootstrap.whenDocumentReady().then(function () {
ko.applyBindings(new ViewModel(), document.getElementById('tabbardemo'));
});
Why? The bootstrapping is required once per application, which is done inside your main.js file.
2) Replace require by define
Why? Require block is again maintained in main.js, where your required modules are pre-loaded. All subsequent viewModels have define block
3) Return an instance of your ViewModel
define([
... Your imports
],
function (Context, ko, Bootstrap, ArrayDataProvider) { // this callback gets executed when all required modules are loaded
function ViewModel() {
// Your code
}
return ViewModel;
});
I have a problem with stripe cashier; i get
This customer has no attached payment source
I don't know why, hope someone could help me... many thanks in advance.
I tried to look a lot of tutorials but i can't get any good results.
Maybe it's a problem with the token ? i tried to enter the credit card test :
4242 4242 4242 4242
I would like to use it as a subscription.
Here my view
<div class="container">
<div class="row">
<script src="https://js.stripe.com/v3/"></script>
<form action="{{route('checkout')}}" method="post" id="payment-form">
{{csrf_field()}}
<div class="form-row">
<label for="card-element">
Credit or debit card
</label>
<div id="card-element">
<!-- a Stripe Element will be inserted here. -->
</div>
<!-- Used to display form errors -->
<div id="card-errors" role="alert"></div>
</div>
<button>Submit Payment</button>
</form>
</div>
</div>
</div>
<script>
// Create a Stripe client
var stripe = Stripe('.....');
// Create an instance of Elements
var elements = stripe.elements();
// Custom styling can be passed to options when creating an Element.
// (Note that this demo uses a wider set of styles than the guide below.)
var style = {
base: {
color: '#32325d',
lineHeight: '18px',
fontFamily: '"Helvetica Neue", Helvetica, sans-serif',
fontSmoothing: 'antialiased',
fontSize: '16px',
'::placeholder': {
color: '#aab7c4'
}
},
invalid: {
color: '#fa755a',
iconColor: '#fa755a'
}
};
// Create an instance of the card Element
var card = elements.create('card', {style: style});
// Add an instance of the card Element into the `card-element` <div>
card.mount('#card-element');
// Handle real-time validation errors from the card Element.
card.addEventListener('change', function(event) {
var displayError = document.getElementById('card-errors');
if (event.error) {
displayError.textContent = event.error.message;
} else {
displayError.textContent = '';
}
});
// Handle form submission
var form = document.getElementById('payment-form');
form.addEventListener('submit', function(event) {
event.preventDefault();
stripe.createToken(card).then(function(result) {
if (result.error) {
// Inform the user if there was an error
var errorElement = document.getElementById('card-errors');
errorElement.textContent = result.error.message;
} else {
// Send the token to your server
stripeTokenHandler(result.token);
}
});
});
</script>
Here my checkout controller :
public function checkout(Request $request)
{
try {
Stripe::setApiKey('.....');
$user = User::find(1);
$user->newSubscription('main', 'monthly')->create($request->stripeToken);
return 'Subscription successful, you get the course!';
} catch (\Exception $ex) {
return $ex->getMessage();
}
}
Here's a simplified example of my knockout model. The problem I'm having is that as soon as the page loads, the quiz is loaded. Why does it get run straight away and how can I stop it so that it only get's run when I want, say, on the click of a button?
Do I even need to use subscribe to do this?
HTML:
<h1>Test</h1>
<button class="btn btn-primary" data-bind="click: quizCount(quizCount() + 1)">
Click Me
</button>
<hr />
<div data-bind="visible: !loaded()">No Quiz</div>
<div data-bind="visible: loaded">Quiz Loaded!</div>
<hr />
<h3>Debug</h3>
<div data-bind="text: ko.toJSON(quizModel)"></div>
Javascript:
<script type="text/javascript">
var quizModel = { };
// DOM ready.
$(function () {
function QuizViewModel() {
var self = this;
self.loaded = ko.observable(false);
self.questions = ko.observable();
self.quizCount = ko.observable();
};
quizModel = new QuizViewModel();
quizModel.quizCount.subscribe(function (newCount) {
$.getJSON('#Url.Action("GetNew", "api/quiz")', function (data) {
quizModel.questions(data.Questions);
}).complete(function () {
quizModel.loaded(true);
});
});
ko.applyBindings(quizModel);
})
</script>
Subscribe is only used for listening to changes in an observable so it will run immediately as soon as the observable gets a value.
You need to add this function to your viewmodel as a method, likely to be called getQuestions:
function QuizViewModel() {
var self = this;
self.loaded = ko.observable(false);
self.questions = ko.observable();
self.quizCount = ko.observable();
self.getQuestions = function(){
$.getJSON('#Url.Action("GetNew", "api/quiz")', function (data) {
self.questions(data.Questions);
}).complete(function () {
self.loaded(true);
});
}
};
then you can easily have a button or something that binds to this method on click:
<button data-bind="click: getQuestions">Get questions</button>
I use RESTAdapter with Ember Data. How to make Ember controller handle server validation errors? I think this code should be in the line "console.log(todo.errors);" but I have no idea how to achieve rendaring needed template...
App.TodosRoute = App.AuthenticatedRoute.extend({
model: function () {
return App.Todo.find();
}
});
App.TodosController = Ember.ArrayController.extend({
createTodo: function(todo) {
var data = this.getProperties('title', 'priority', 'due_date');
var todo = App.Todo.createRecord(data);
var self = this;
todo.on('becameInvalid', function(todo) {
// show errors on the form. code goes here
console.log(todo.errors);
});
todo.on('didCreate', function() {
// render list. code goes here
self.set('title', '');
self.set('priority', '');
self.set('due_date', '');
});
todo.save();
}
});
<script type="text/x-handlebars" data-template-name='todo/_edit'>
{{input type='number' class="input" placeholder="Priority" value=priority}}
{{input class="input" placeholder="What needs to be done?" value=title}}
{{input type='date' class="input" placeholder="Due date" value=due_date}}
</script>
<script type="text/x-handlebars" data-template-name="todos">
<section id="todoapp">
<header id="header">
{{partial 'todo/edit'}}
<button {{action "createTodo"}}>Save</button>
</header>
<section id="main">
<ul id="todo-list" class="sortable">
{{#each controller itemController='todo'}}
{{#unless isNew}}
<li {{bindAttr class="isEditing:editing"}} data-id="{{unbound id}}">
{{#if isEditing}}
{{partial 'todo/edit'}}
<button {{action "saveTodo"}} class="save-button">Save</button>
{{else}}
<label>{{priority}}</label>
<label>{{title}}</label>
<label>{{date due_date}}</label>
<button {{action "editTodo"}} class="edit-button">Edit</button>
<button {{action "removeTodo"}} class="destroy">Delete</button>
{{/if}}
</li>
{{/unless}}
{{/each}}
</ul>
</section>
</section>
</script>
Finally I got code like this:
App.TodosNewController = Ember.ObjectController.extend({
create: function() {
var data = this.getProperties('title', 'priority', 'due_date');
var todo = App.Todo.createRecord(data);
var self = this;
todo.on('becameInvalid', function(todo) {
self.set('model', todo);
});
todo.on('didCreate', function() {
self.set('priority', '');
self.set('title', '');
self.set('due_date', '');
self.transitionToRoute('todos');
});
todo.save();
}
});
Hope, it will help somebody
I have a telerik popped up window. It opens fine; however, I am having an issue closing the window. If I add an alert to the javascript, alert("#Window") or alert($(this).closest('#Window')), it will display [object Object]. However, alter("#Window").data("tWindow") or alert($(this).closest('#Window').data('tWindow')) will display null. I've removed the jquery and javascript reference from either the parent or the child page, and it did not make any difference. Any kind of help would be greatly appreciated. See sample code below
Here is the popup window:
#{Html.Telerik().Window()
.Name("Window")
.Title("Student Window")
.LoadContentFrom(Url.Action("AddReason", "Reason", new { id = reasonID }, Request.Url.Scheme))
.ClientEvents(events => events
.OnClose("ClosingWindow")
)
.Draggable(false)
.Scrollable(false)
.Width(800)
.Height(600)
.Modal(true)
.Visible(false)
//.Effects(fx => fx
// .Zoom()
// .Opacity())
.Render();
}
Here is the JavaScript:
<script src="#Url.Content("~/Scripts/jquery-1.4.4.js")" type="text/javascript">
</script>
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/spin.min.js")" type="text/javascript"></script>
function DoOpen(id) {
var url = '#Url.Action("AddReason","Reason")';
$.post(url, { id: id }, function (data) {
var window = $('#Window').data('tWindow').center();
window.content(data);
window.open();
});
}
//This javascript is in the main page
//I did an alert. alert($('#Window')) and
alert($('#Window').data('tWindow')) they both return null
function ClosingWindow() {
$('#Window').prop("checked", "checked");
$('#Window').data('tWindow').close();
window.location.href = window.location.href;
}
Here is the partial view :
#model Student.Models.Reason
#using Student.Example
#{
ViewBag.Title = "Add Reason";
Layout = "~/Views/Shared/_PartialReason.cshtml";
}
<script type="text/javascript">
function CloseWindow() {
// alert($("#Window").closest('.t-window').data('#tWindow'));
// $("#Window").data("tWindow").close();
$('#Window').prop("checked", "checked");
window.location.href = window.location.href;
}
</script>
#using (Html.BeginForm("AddReason", "Reason", FormMethod.Post))
{
#Html.ValidationSummary(true)
<fieldset>
<div class="editor-field">
#(Html.Telerik().Editor()
.Name("EncountersArchive")
.HtmlAttributes(new { style = "height:310px;", id = "AddAReason" })
.Encode(true)
.Tools(
tools => tools
.Clear()
.Bold().Italic().Underline().Strikethrough().Subscript().Superscript().Separator()
.FontName().FontSize()
.FontColor().BackColor().Separator()
.JustifyLeft().JustifyCenter().JustifyRight().JustifyFull().Separator()
.InsertUnorderedList().InsertOrderedList().Separator()
.Indent().Outdent().Separator()
))
</div>
<p style="text-align:center">
<input type="submit" value="Reason" id="AddReasonID" onclick="CloseWindow()"/>
</p>
</fieldset>
}
The OnClose event on the popup window was causing the issue. The onclose events should only be used if the user wished to do someonthing else after the window is closed. If the purpose is simply to close the window, the Telerik control is automatically handling it. In my case, I simply remove the onclose event, and it works like a charm.