I have some one-way binding issues while trying to bind a value in my view to a value in my controller:
App.ApplicationController = Ember.Controller.extend({
value: 'my value'
});
App.ApplicationView = Ember.View.extend({
templateName: 'application',
willInsertElement: function() {
console.log('will insert element');
console.log('value from controller > ', this.get('controller').get('value'));
console.log('value > ', this.get('value'));
console.log('completeValue > ', this.get('completeValue'));
},
valueBinding: Ember.Binding.oneWay('controller.value'),
completeValueBinding: Ember.Binding.oneWay('App.ApplicationController.value')
});
"value >" returns the right value but "completeValue >" returns undefined (see jsFiddle http://jsfiddle.net/U29wV/7/)...
You are referencing the class ApplicationController not the actual instance.
You are already accessing the ApplicationController instance through the controller property of your view, so just use that for the binding:
valueBinding: Ember.Binding.oneWay('controller.value'),
completeValueBinding: Ember.Binding.oneWay('controller.value')
Which makes it identical to the valueBinding...
Related
I am trying to patch the object received from webapi to an angular reactive form. The form also has a formarray. But, despite having more than 3 or more records only 2 records are being patched to the formarray of the reactive form.
I have two entities noseries and noseriesList, where a noseries has zero or many noseriesLists. So after obtaining noseries from webapi, i want to patch properties and navigation list "noseriesLists" of noseries into reactive form.
Rest of the properties are being patched properly, but only 2 records of navigation list "noseriesLists" is being patched to the formArray nested inside the reactive form.
//initialization of form
this.noseriesForm = this.fb.group({
id: [null],
description: ['', Validators.required],
code: [ '', Validators.compose([Validators.maxLength(10), Validators.required])],
noSeriesList: this.fb.array([
this.initLine(), this.initLine()
])
});
//patching the object received from service to form
this.route.params.subscribe(routeParam => {
if (routeParam.id) {
this.noseriesService.get(routeParam.id)
.subscribe((data: NoSeries) => {
this.isCreateMode = false;
this.noseries = data;
this.noseriesForm.patchValue(this.noseries);
console.log(this.noseries, 'data from api');
console.log(this.noseriesForm.value,'formvalue');
});
}
});
//initialise formArray
initLine(): FormGroup {
return this.fb.group({
id: [null],
startingNoSeries: ['', Validators.required],
endingNoSeries: '',
lastUsedSeries: '',
effectiveDate: [null],
endingDate: [null],
noSeriesId: [null]
});
}
logging the data received from service shows 3 noseriesList records, whereas logging formvalue only shows 2 records of noseriesList.
while you are initializing the form array for the first time you are adding two empty controls. that's why when you patch value to formgroup, only those two empty controls get filled. you should fill the formarray with number of controls that is going to be patched before patching a value.
//patching the object received from service to form
this.route.params.subscribe(routeParam => {
if (routeParam.id) {
this.noseriesService.get(routeParam.id).subscribe((data: NoSeries) => {
this.isCreateMode = false;
this.noseries = data;
const nsList = this.noseriesForm.get("noSeriesList") as FormArray;
nsList.clear();
this.noseries.forEach(_ => nsList.push(this.initLine()));
this.noseriesForm.patchValue(this.noseries);
console.log(this.noseries, 'data from api');
console.log(this.noseriesForm.value,'formvalue');
});
}
});
I would like to use knockout-validation by only adding validation rules in HTML5, which works great:
http://jsfiddle.net/gt228dgm/1/
I would then like to change the default error messages generated by the browser (like "This field is required." or "Invalid"). This is pretty easily done in javascript code, But I believe this kind of texts should go into the HTML. Is that possible and how? I guess I'm looking for something like:
<input data-bind='value: firstName, validate: { message: "Please enter first name" }' required pattern="^[A-Za-z]{1,255}$"/></label>
I have implemented a custom knockout binding that solves my issue:
ko.bindingHandlers.validate = {
init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
var valueBinding = allBindings().value;
var value = valueAccessor();
if (value) {
valueBinding.extend(value);
}
}
};
The binding then looks like this
<input data-bind='value: firstName, validate: { required: { message: 'Full name is missing' }, pattern: { message: 'Full name should be max. 255 alphanumeric characters' } }' required pattern="^[A-Za-z]{1,255}$"/>
Any other suggestions?
I've been playing these days with knockout validation. For your need, you could use validationMessage binding. Here is an example of your code modified, to use this binding: http://jsfiddle.net/elbecita/gt228dgm/4/
Basically, you'd end up having something like:
<span data-bind="validationMessage: firstName, text: 'Your error.'"></span>
<input data-bind='value: firstName, valueUpdate: "input"' required pattern="^[A-Za-z]{1,255}$" />
I've added the valueUpdate binding just to have the validation message shown immediately. And then, in your javascript code, when you define the validation configuration, be sure to turn to false the insertMessages option. If not, you'll see the default message too.
But... I think is easier and cleaner to do this with the knockout extenders in javascript code, having all custom messages centralized in some sort of constants file, instead of having them scattered through the html.
Anyway, dig into the knockout validation bindings, they may help you with your needs. :)
Proper way to handle custom validation message in KO is to create a custom validation not binding.
Here is an example of simple number validation
ko.validation.rules['integerFormat'] = {
validator: function (value, options) {
if (!value) return true;
var regex = /^\d+$/;
var matches = regex.test(value);
if(matches){
value = parseInt(value,10);
return typeof value== "number" && isFinite(value) && value%1===0;
}else{
return false
}
},
message: app.MyCustomIntegerNotValidMSG
}
app.MyCustomIntegerNotValidMSG is just a variable holding your MSG
You would use this in your ViewModel like this
self.myIntegerToValidate = ko.observable().extend({
integerFormat:{}
});;
I am a complete beginner, so please excuse if this question is maybe phrased incorrectly or I am using the wrong terms for certain things.
I have got an MS Word Document in which there are, say, four-hundred diary entries. The software I want to write enables the user to search for specific terms within one entry. So I might want to have all the diary entries that contain both the words "happy" and "sad". Another option I want to include is to search for all diary entries written between e.g. 2008 and 2012 etc.
I would like the search to be 'dynamic', in the sense that the user might type in one word, and while that word is being typed the results-table already filters all the diary entries. So the table changes while the user is typing the word, according to what is currently being typed in the search box.
Is this possible & what exactly is this feature called? What programming language would you recommend me? I would like to have all of this online, so maybe php or ruby would be useful?
Your choice of Ruby-on-Rails is apt for this issue:
Store each diary entry as a database entry
Search these entries using "full text search" - either on the db, or third party
Return the "live" functionality with JS
MVC
Rails uses MVC programming pattern to give you ability to save into the database. This is important because if you're going to develop in rails, you'll need to keep to the MVC pattern:
Basically, you keep your Diary entries in the database, use the controller to manipulate the data & use the view to show the data:
#app/models/entry.rb
Class Entry < ActiveRecord::Base
#-> stores & brings back the entry data
end
#app/controllers/entries_controller.rb
Class EntriesController < ApplicationController
respond_to :js, :html, :json, only: :search
def index
#entries = Entry.all
end
def search
#entries = Entry.search params[:query]
respond_with #entries
end
end
#config/routes.rb
resources :entries do
collection do
match :search, via [:get, :post]
end
end
--
Full Text Search
When you send the request to your controller, the way you'll handle the search will be with a class method in your Entry model (that's where you get your data from), either referencing -
#app/models/entry.rb
Class Entry < ActiveRecord::Base
def self.search(query)
where("title LIKE '%#{query}%'")
end
end
You can either use full text search for the SQL variant you're using, or use a third party search, like Solr or something
--
"Live" Search
You can get "live" search working with Javascript (example here):
There are a number of tutorials on how to do this online - just Google live search rails or autocomplete rails. However, the principle is basically the same for all implementations:
JS will capture the text entered into a search box
JS sends Ajax request to your Rails controller
Controller sends response to ajax
JS takes response & shows on screen
Notice how this is primarily focused on the JS element? Here's the code we use:
#app/assets/javascripts/application.js
//Livesearch
$(document).ready( function() {
var base_url = window.location.protocol + "//" + window.location.host;
$('#SearchSearch').searchbox({
url: base_url + '/search/',
param: 'search',
dom_id: '#livesearch',
loading_css: '#livesearch_loading'
})
});
#app/assets/javascripts/livesearch.js
// Author: Ryan Heath
// http://rpheath.com
(function($) {
$.searchbox = {}
$.extend(true, $.searchbox, {
settings: {
url: 'search',
param: 'search',
dom_id: '#livesearch',
minChars: 2,
loading_css: '#livesearch_loading',
del_id: '#livesearch_del'
},
loading: function() {
$($.searchbox.settings.loading_css).show()
},
idle: function() {
$($.searchbox.settings.loading_css).hide()
},
start: function() {
$.searchbox.loading()
$(document).trigger('before.searchbox')
},
stop: function() {
$.searchbox.idle()
$(document).trigger('after.searchbox')
},
kill: function() {
$($.searchbox.settings.dom_id).fadeOut(50)
$($.searchbox.settings.dom_id).html('')
$($.searchbox.settings.del_id).fadeOut(100)
},
reset: function() {
$($.searchbox.settings.dom_id).html('')
$($.searchbox.settings.dom_id).fadeOut(50)
$('#SearchSearch').val('')
$($.searchbox.settings.del_id).fadeOut(100)
},
process: function(terms) {
if(/\S/.test(terms)) {
$.ajax({
type: 'GET',
url: $.searchbox.settings.url,
data: {search: terms.trim()},
complete: function(data) {
$($.searchbox.settings.del_id).fadeIn(50)
$($.searchbox.settings.dom_id).html(data.responseText)
if (!$($.searchbox.settings.dom_id).is(':empty')) {
$($.searchbox.settings.dom_id).fadeIn(100)
}
$.searchbox.stop();
}
});
return false;
}else{
$.searchbox.kill();
}
}
});
$.fn.searchbox = function(config) {
var settings = $.extend(true, $.searchbox.settings, config || {})
$(document).trigger('init.searchbox')
$.searchbox.idle()
return this.each(function() {
var $input = $(this)
$input
.keyup(function() {
if ($input.val() != this.previousValue) {
if(/\S/.test($input.val().trim()) && $input.val().trim().length > $.searchbox.settings.minChars){
$.searchbox.start()
$.searchbox.process($input.val())
}else{
$.searchbox.kill()
}
this.previousValue = $input.val()
}
})
})
}
})(jQuery);
If I loop the collection in the view, it's seems empty, alert dialog don't show up. When I use console.log(this.collection) in this view, it's look ok (16 element in this collection).
My router: (collection url: '/api/employees', this is a rails json output)
Office.Routers.Employees = Backbone.Router.extend({
routes: {
"": "index"
},
initialize: function() {
this.collection = new Office.Collections.Employees();
this.collection.fetch();
},
index: function() {
var view = new Office.Views.EmployeesIndex({ collection: this.collection });
view.render();
}
});
and my index.js view:
Office.Views.EmployeesIndex = Backbone.View.extend({
render: function() {
this.collection.each( function( obj ){ alert(obj); } );
}
});
Edit:
This is the output of the console.log(this.collection) in view : http://i.stack.imgur.com/ZQBUD.png
Edit2:
I thing Rails is the guilty. When I work whit static collection, everything works fine
var collection = new Backbone.Collection([
{name: "Tim", age: 5},
{name: "Ida", age: 26},
{name: "Rob", age: 55}
]);
collection.fetch() makes an asynchronous request to the server. The index callback doesn't wait for the fetch to return. So your render function is rendering an empty collection.
You need to use the success callback of the fetch method:
index: function() {
this.collection.fetch({
success: function(data) {
var view = new Office.Views.EmployeesIndex({ collection: data });
view.render();
}
});
}
Note that the Backbone documentation recommends bootstrapping any initial data you need by including the data in the document itself:
When your app first loads, it's common to have a set of initial models
that you know you're going to need, in order to render the page.
Instead of firing an extra AJAX request to fetch them, a nicer pattern
is to have their data already bootstrapped into the page.
The fetch has probably not completed by the time your view renders. Try the following:
index: function() {
var p, collection, view;
collection = new Office.Collections.Employees();
p = collection.fetch();
view = new Office.Views.EmployeesIndex({ collection: collection });
p.done(view.render);
}
I'm trying to add controller to my Todo list app. Here's the code.
$(function(){
alert(Backbone); // => [object]
alert(Backbone.Controller); // => undefined
TodoList.Controllers.Todos = Backbone.Controller.extend({
routes: {
"documents/:id": "edit",
"": "index",
"new": "newDoc"
},
edit: function(id){
var todo = new Todo({id:id});
todo.fetch({
success:function(model,resp){
new App.Views.Edit({model:todo});
},
error: function(){
new Error({message: "Couldn't find the todo item."});
window.location.hash = '#';
}
});
},
index: function(){
window.App = new TodoList.Views.AppView
}
});
});
As mentioned in the comment, when I alert(Backbone), [object] is returned, while Backbone.Controller returns undefined and I can't figure out why. This is stopping the whole app to work.
It's been replaced with Backbone Router: http://backbonetutorials.com/what-is-a-router/
which backbone version do you have?
Try Backbone.Router instead of Backbone.Controller.
Quote from http://backbonejs.org : as of v5.0
Controller was renamed to Router, for clarity
If that's the case, you can make a reference copy of the Router in order to not alter the existing code :
if(Backbone.Router)
Backbone.controller = Backbone.Router;