How to invoke "{Model.Foo} updated" after model update? - javascript

I have a javascriptMVC Model
/**
* #class Hzfrontend.Models.Warmwasser
* #parent index
* #inherits jQuery.Model
* Wraps backend warmwasser services.
*/
$.Model('Hzfrontend.Models.Warmwasser',
/* #Static */
{
findAll: "/api/warmwasser",
findOne : "/api/warmwasser/{id}",
update : "/api/warmwasser/{id}"
},
/* #Prototype */
{
update : function(attrs, success, error){
$.ajax({
type: 'PUT',
url:"/api/warmwasser/"+this.id,
data: $.toJSON(this),
success: success,
error: error,
dataType: "json"});
}
});
and a Controller:
$.Controller('Hzfrontend.Warmwasser.List',
/** #Static */
{
defaults : {}
},
/** #Prototype */
{
init : function(){
this.element.append(this.view('init',Hzfrontend.Models.Warmwasser.findAll()) )
},
'.waterTemperature change': function( el ){
var mod = el.closest('.warmwasser').model();
mod.attr('waterTemperature', el.val());
mod.update();
steal.dev.log("update waterTemperature");
},
"{Hzfrontend.Models.Warmwasser} updated" : function(Warmwasser, ev, warmwasser){
warmwasser.elements(this.element)
.html(this.view('warmwasser', warmwasser) );
steal.dev.log("updated");
}
});
after the update completed I want to invoke the updated callback in the controller. How to do this?
If I use the ./fixtures/fixtures.js' to test the app without a server backend, it works without any problems.
Thanks.

That code should work. What do you return as a response from your server? You should return an JSON response with the object that you updated.

You shouldn't have a prototype update method.

Related

Call Restlet from User Event Script Error INVALID_LOGIN_ATTEMPT

I am trying to access Restlet from User Event script but i am receiving error: body":"error code: INVALID_LOGIN_ATTEMPT\nerror message: Invalid login attempt. Do i need to pass credentials too? what should i pass client id, client secret? is there another way?
I also tried url.resolveScript but no luck.
/**
* #NApiVersion 2.x
* #NScriptType UserEventScript
* #NModuleScope Public
*/
define(["N/https", "N/log", "N/record", "N/url" ],
function(https, log, record, url) {
function beforeLoad(context) {
// log.debug('beforeLoad Triggered');
// context.newRecord;
// context.type;
// context.form;
return;
}
function afterSubmit(context) {
log.debug('Before code');
var record = context.newRecord;
var requestBody = {
"recordId": record.id,
"recordType": record.type,
};
var output = url.resolveScript({
scriptId: '1157',
deploymentId: '1',
});
var output1 = 'https://12345-sb5.restlets.api.netsuite.com';
log.debug('After code', output );
var response = https.post({
url: output1 + output,
body: requestBody,
});
if (response.code === 200) {
// success!
} else {
// handle error
}
log.debug('After code', response );
return;
}
function beforeSubmit(context) {
// log.debug('beforeSubmit Triggered');
// context.newRecord;
// context.oldRecord;
// context.type;
return;
}
return {
beforeLoad : beforeLoad,
afterSubmit : afterSubmit,
beforeSubmit : beforeSubmit
}
})
Why do you need your UE Script to submit and trigger your RESTlet every record submit?
Below could work for ClientScript, just not sure if saveRecord (only when the Submit/Save is clicked) entry point will trigger this.
var requestBody = JSON.stringify({
recordId: record.id,
recordType: record.type
});
var output = url.resolveScript({
scriptId: '1157',
deploymentId: '1'
});
var response = https.post({
url: output,
body: requestBody
});
Previously you could manipulate the cookies and pass it. However, it's no longer the case and Server Scripts cannot call Server Scripts unless you pass Authentication which is a pain if you have no library. Oauth1 + SHA256 is complicated to figure out without libraries.
You could probably approach this on another angle depending on your use case. At this moment, it isn't clear why you want AfterSubmit to call RESTlet. If you do this, if you have routine Scheduled Scripts that touches that record and saves the record, it will keep triggering your AfterSubmit unless you place an if statement.
try https.requestRestlet
var response = https.requestRestlet({
body: JSON.stringify({
"recordId": record.id,
"recordType": record.type,
}),
deploymentId: '1',
method: 'POST',
scriptId: 1157,
});

mock JSON response in Backbone Fetch?

I'm learning Backbone and want to "mock" the results of a .fetch() call within a model. I do not want to use a testing library or actually hit an external service.
Basically I have a setting in my model, where if this.options.mock === true, then just use an internal JSON object as the "result" of the fetch. Else, actually hit the API with a real AJAX request.
However, this doesn't seem to work. My view successfully renders with the model data when I hit the actual API ("real" fetch), but not whenever I try and pass in fake data.
Is there a way to fake a Fetch response in Backbone, without bringing in a testing library like Sinon?
here is the complete model (at least the relevant portions of it). Basically, the model fetches data, and formats it for a template. and then the view which owns the model renders it out.
'use strict';
(function (app, $, Backbone) {
app.Models.contentModel = Backbone.Model.extend({
/**
* Initializes model. Fetches data from API.
* #param {Object} options Configuration settings.
*/
initialize: function (options) {
var that = this;
that.set({
'template': options.template,
'mock': options.mock || false
});
$.when(this.retrieveData()).then(function (data) {
that.formatDataForTemplate(data);
}, function () {
console.error('failed!');
});
},
retrieveData: function () {
var that = this, deferred = $.Deferred();
if (typeof fbs_settings !== 'undefined' && fbs_settings.preview === 'true') {
deferred.resolve(fbs_settings.data);
}
else if (that.get('mock')) {
console.info('in mock block');
var mock = {
'title': 'Test Title',
'description': 'test description',
'position': 1,
'byline': 'Author'
};
deferred.resolve(mock);
}
else {
// hit API like normal.
console.info('in ajax block');
that.fetch({
success: function (collection, response) {
deferred.resolve(response.promotedContent.contentPositions[0]);
},
error: function(collection, response) {
console.error('error: fetch failed for contentModel.');
deferred.resolve();
}
});
}
return deferred.promise();
},
/**
* Formats data on a per-template basis.
* #return {[type]} [description]
*/
formatDataForTemplate: function (data) {
if (this.get('template') === 'welcomead_default') {
this.set({
'title': data.title,
'description': data.description,
'byline': data.author
});
}
// trigger the data formatted event for the view to render.
this.trigger('dataFormatted');
}
});
})(window.app, window.jQuery, window.Backbone);
Relevant bit from the view (ContentView):
this.model = new app.Models.contentModel({template: this.templateName});
this.listenTo(this.model, 'dataFormatted', this.render);
Is the data being set so fast that the listener hasn't been set up yet?
You can override the fetch function like this.
var MockedModel = Backbone.Model.extend({
initialize: function(attr, options) {
if (options.mock) {
this.fetch = this.fakeFetch;
}
},
url: 'http://someUrlThatWIllNeverBeCalled.com',
fakeFetch: function(options) {
var self = this
this.set({
'title': 'Test Title',
'description': 'test description',
'position': 1,
'byline': 'Author'
});
if (typeof options.success === 'function') {
options.success(self, {}, {})
}
}
});
var mockedModel = new MockedModel(null, {
mock: true
})
mockedModel.fetch({
success: function(model, xhr) {
alert(model.get('title'));
}
});
<script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.2/underscore-min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/backbone.js/1.1.2/backbone-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Problem here isn't with the actual implementation of retrieveData but with the way it's being called. When you resolve the deferred before returning you're basically making it instant. This leads to formatDataForTemplate being called while your model is still initializing.
So when you do
this.model = new app.Models.contentModel({template: this.templateName});
this.listenTo(this.model, 'dataFormatted', this.render);
The dataFormatted event ends up being triggered before the listener has registered.
One solution is to use a timeout which should work with just
setTimeout(function() {
deferred.resolve(mock);
});
as that will delay the resolve untill the next round of the event loop when the listener is in place.
Another solution, not involving the setTimeout would be to not call retrieveData during model initialization but rather let the view do it after it has attached its listeners.
this.model = new app.Models.contentModel({template: this.templateName});
this.listenTo(this.model, 'dataFormatted', this.render);
this.model.retrieveData();
I would prefer the latter but if this is just about mocking data to work offline it doesn't really matter in my opinion.
Unrelated to that it's worth noting that the actual signature for initialize on a model is new Model([attributes], [options]) so your initialize should probably look like this
initialize: function (attributes, options) {
var that = this;
that.set({
'template': options.template,
'mock': options.mock || false
});
Just for the sake of readability. That again means that since you are passing only one object you should not need to call set at all.

FOSUserBundle AJAX Login with Symfony2 (routing)

I'm trying to make the AJAX authentication work with FOSUserBundle.
I have created an Handler directory with a AuthenticationHandler class :
<?php
namespace BirdOffice\UserBundle\Handler;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\SecurityContextInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;
class AuthenticationHandler implements AuthenticationSuccessHandlerInterface, AuthenticationFailureHandlerInterface
{
private $router;
private $session;
/**
* Constructor
*
* #param RouterInterface $router
* #param Session $session
*/
public function __construct( RouterInterface $router, Session $session )
{
$this->router = $router;
$this->session = $session;
}
/**
* onAuthenticationSuccess
*
* #param Request $request
* #param TokenInterface $token
* #return Response
*/
public function onAuthenticationSuccess( Request $request, TokenInterface $token )
{
// if AJAX login
if ( $request->isXmlHttpRequest() ) {
$array = array( 'success' => true ); // data to return via JSON
$response = new Response( json_encode( $array ) );
$response->headers->set( 'Content-Type', 'application/json' );
return $response;
// if form login
} else {
if ( $this->session->get('_security.main.target_path' ) ) {
$url = $this->session->get( '_security.main.target_path' );
} else {
$url = $this->router->generate( 'home_page' );
} // end if
return new RedirectResponse( $url );
}
}
/**
* onAuthenticationFailure
*
* #param Request $request
* #param AuthenticationException $exception
* #return Response
*/
public function onAuthenticationFailure( Request $request, AuthenticationException $exception )
{
// if AJAX login
if ( $request->isXmlHttpRequest() ) {
$array = array( 'success' => false, 'message' => $exception->getMessage() ); // data to return via JSON
$response = new Response( json_encode( $array ) );
$response->headers->set( 'Content-Type', 'application/json' );
return $response;
// if form login
} else {
// set authentication exception to session
$request->getSession()->set(SecurityContextInterface::AUTHENTICATION_ERROR, $exception);
return new RedirectResponse( $this->router->generate( 'login_route' ) );
}
}
}
I have created a login Javascript function :
function login() {
$.ajax({
type: "POST",
url: Routing.generate('check_login_ajax'),
dataType: 'json',
data: {
_username: $('#username').val(),
_password: $('#password').val(),
_remember_me: false,
_csrf_token: $('#_csrf_token').val()
}
}).done(function(data) {
console.log(data);
}).fail(function(data) {
console.log(data);
});
}
In my routingAjax.yml, I have added the following lines to override the FOSUserBundle security route :
check_login_ajax:
pattern: /check_login_ajax
defaults: { _controller: FOSUserBundle:Security:check }
requirements:
_method: POST
options:
expose: true
In my global security.yml file, I have added the check_path, success_handler and failure_handler parts :
firewalls:
main:
pattern: ^/
form_login:
login_path: fos_user_registration_register
check_path: check_login_ajax
success_handler: user.security.authentication_handler
failure_handler: user.security.authentication_handler
provider: fos_userbundle
csrf_provider: form.csrf_provider
logout:
path: fos_user_security_logout
target: /
anonymous: true
My first issue is : the AJAX return this message: "Invalid CSRF token." (but I send a good one generated in PHP, maybe I missed something doing it). Here is my PHP code for it :
<?php
$csrfProvider = $this->container->get('form.csrf_provider');
$csrfToken = $csrfProvider->generateCsrfToken('popUpUser');
?>
Second issue : my login page (not the AJAX one) is not working anymore because the orignal route of FOSUserBundle login has been overwritten.
PS : I have posted a message yesterday : FOSUserBundle (login / register) + AJAX + Symfony2 but I have badly explained my problem. Sorry by advance.
First Issue: You are sending an invalid CSRF token. In Symfony 2.3 you could generate it using {{ csrf_token('authenticate') }} inside the template's input's value.
Second issue: Do not overwrite the route, simply use the original route: fos_user_security_check.
In general: if you use an AuthenticationSuccessHandler extending Symfony\Component\Security\Http\Authentication\DefaultAuthenticationSuccessHandler your method could look something like this:
public function onAuthenticationSuccess(Request $request, TokenInterface $token)
{
if ($request->isXmlHttpRequest()) {
return new JsonResponse(array('success' => true));
}
return parent::onAuthenticationSuccess($request, $token);
}
Do something similar for an AuthenticationFailureHandler extending Symfony\Component\Security\Http\Authentication\DefaultAuthenticationFailureHandler.

Angular, Laravel 4 and MySQL database best practices

I am pretty new to Angular and have a bit of experience with Laravel 4.
I am building an app where the user can edit "on-the-fly" but also save to a MySQL.
The initial plan was to use Angular to manage the live editing, and to store and retrieve data to the MySQL DB using Eloquent. I am aware you can connect to DB via Angular, but I'd like to know what the best solution would be.
Is it best to keep it separated or to use Angular for everything?
Is there some performance issues if using Angular for everything?
Is Angular as easy to use as Eloquent for DB interactions ?
If I use Eloquent for DB, is it straight-forward to pass data to Angular?
I have already started building the live-editing part of the app with Angular and I have to say I found it very easy to learn and extremely powerful. I now have to make a decision as to how I will handle storage.
Thank you
Check out this tutorial by the great Dave Mosher, I think it might be exactly what you're looking for, he uses Laravel, Angular, and MySQL:
Youtube Screencast: http://www.youtube.com/watch?v=hqAyiqUs93c
Source code: https://github.com/davemo/end-to-end-with-angularjs
The best way to use angular.js and laravel is, using a REST API.
For example, if you have an admin panel to manage users, the method will be,
In your route,
Route::resource('users', 'UsersController');
The controller looks like this,
<?php
/**
*
* Users Controller
*
*/
class UsersController extends AdminController {
/**
* Display all users.
*
* #return Response
*/
public function index() {
$users = User::where('id', '!=', Auth::user()->id)->get();
return Response::json(array(
'status' => 'success',
'users' => $users->toArray()),
200
);
}
/**
* Show the form for creating a new resource.
*
* #return Response
*/
public function create() {
//
}
/**
* Store a newly created resource in storage.
*
* #return Response
*/
public function store() {
// add some validation also
$input = Input::all();
$user = new User;
if ( $input['name'] ) {
$user->name = $input['name'];
}
if ( $input['username'] ) {
$user->username = $input['username'];
$user->password = Hash::make($input['username']);
}
if ( $input['email'] ) {
$user->email = $input['email'];
}
$user->save();
return Response::json(array(
'status' => 'success',
'users' => $user->toArray()),
200
);
}
/**
* Display the specified resource.
*
* #param int $id
* #return Response
*/
public function show($id) {
$user = User::where('id', $id)
->take(1)
->get();
return Response::json(array(
'error' => false,
'users' => $user->toArray()),
200
);
}
/**
* Show the form for editing the specified resource.
*
* #param int $id
* #return Response
*/
public function edit($id) {
//
}
/**
* Update the specified resource in storage.
*
* #param int $id
* #return Response
*/
public function update($id) {
// add some validation also
$input = Input::all();
$user = User::find($id);
if ( $input['name'] ) {
$user->name = $input['name'];
}
if ( $input['username'] ) {
$user->username = $input['username'];
}
if ( $input['email'] ) {
$user->email = $input['email'];
}
$user->save();
return Response::json(array(
'status' => 'success',
'message' => 'User Updated'),
200
);
}
/**
* Remove the specified resource from storage.
*
* #param int $id
* #return Response
*/
public function destroy($id) {
$user = User::find($id);
$user->delete();
return Response::json(array(
'status' => 'success',
'message' => 'User Deleted'),
200
);
}
}
Then the script,
var app = angular.module('myApp', []);
// include this in php page to define root path
app.factory('Data', function(){
return {
root_path: "<?php echo Request::root(); ?>/"
};
});
GET - Get all users
$http({method: 'GET', url: Data.root_path + 'users'}).
success(function(data, status, headers, config) {
$scope.users = data.users;
}).
error(function(data, status, headers, config) {
$scope.users = [];
});
GET - Get single user for edit
$http({method: 'GET', url: Data.root_path + 'users/'+id}).
success(function(data, status, headers, config) {
$scope.entry = data.users[0];
}).
error(function(data, status, headers, config) {
$scope.entry = [];
});
PUT - Update single user
$http.put(Data.root_path + 'users/'+entry.id, entry).
success(function(data, status, headers, config) {
//
}).
error(function(data, status, headers, config) {
//
});
POST - Save new user
$http.post(Data.root_path + 'users', entry).
success(function(data, status, headers, config) {
//
}).
error(function(data, status, headers, config) {
//
});
DELETE - Delete a user
$http.delete(Data.root_path +'users/'+id)
.success(function(response) {
//
})
.error(function(response) {
//
});
Create RESTful endpoints to manage resources using Laravel. This is dead simple with the resource controller as already pointed out:
Route::resource('user', 'UsersController');
Then you use an AngularJS resource to interact with that. Example (from docs) of fetching a user, editing the model and then saving (calling via the $ in the callback).
var User = $resource('/user/:userId', {userId:'#id'});
var user = User.get({userId:123}, function() {
user.abc = true;
user.$save();
});
http://docs.angularjs.org/api/ngResource.$resource
Even better, create an Angular JS Service for the resource so you can just inject that into multiple controllers and get all the methods defined.
Here's a great post on how to set this up:
http://blog.brunoscopelliti.com/building-a-restful-web-service-with-angularjs-and-php-more-power-with-resource

is it possible to include an event in a javascript function?

i was just wondering if getting a jqgrid event from a main javascript and separate it using another javascript in a form of function would work? what im trying to do is like this. i have a code :
...//some code here
serializeGridData: function(postData) {
var jsonParams = {
'SessionID': $('#eSessionID3').val(),
'dataType': 'data',
'recordLimit': postData.rows,
'recordOffset': postData.rows * (postData.page - 1),
'rowDataAsObjects': false,
'queryRowCount': true,
'sort_fields': postData.sidx
};
if (postData.sord == 'desc')
{
...//some code here
}
else
{
...//some code here
}
return 'json=' + jsonParams;
},
loadError: function(xhr, msg, e) {
showMessage('errmsg');
},
...//some code here
i want to get this code and write this in another javascript file and make this as a function, so that my other file could use this one..is it possible?
i created something like this in my other javascrtip file where i planned to put all my functions. here's the code (functions.js):
function serialLoad(){
serializeGridData: function(postData) {
var jsonParams = {
'SessionID': $('#eSessionID3').val(),
'dataType': 'data',
'recordLimit': postData.rows,
'recordOffset': postData.rows * (postData.page - 1),
'rowDataAsObjects': false,
'queryRowCount': true,
'sort_fields': postData.sidx
};
if (postData.sord == 'desc')
{
...//some code here
}
else
{
...//some code here
}
return 'json=' + jsonParams;
},
loadError: function(xhr, msg, e) {
showMessage('errmsg');
}
}
this isn't working and display a message syntax error. i don't know how to correct this. is there anyone who can help me.?
First of all the answer on your derect question. If you define in the functions.js file some global variable, for example, myGlobal:
myGlobal = {};
myGlobal = serializeGridData: function(postData) {
// ... here is the implementation
};
you can use it in another JavaScript file which must be included after the functions.js file:
serializeGridData: myGlobal.serializeGridData
(just use such parameter in the jqGrid definition).
If you want to use the serializeGridData parameter with the value in the most of your jqGrids you can overwrite the default value of serializeGridData in the functions.js file instead:
jQuery.extend(jQuery.jgrid.defaults, {
datatype: 'json',
serializeGridData: function(postData) {
// ... here is the implementation
},
loadError: function(xhr, msg, e) {
showMessage('errmsg');
}
});
In the example I ovewride additionally default datatype: 'xml' jqGrid parameter to datatype: 'json'. It shows that in the way you can set default values of any jqGrid parameter.
What it seems to me you really need is to use prmNames jqGrid parameter to rename some defaulf names of the standard jqGrid parameters. For example with
prmNames: {
rows:"recordLimit",
sort: "sort_fields",
search:null,
nd:null
}
you rename the standard rows parameter to recordLimit, the sidx to sort_fields and remove _search and nd parameters to be send.
Additionally you can use postData having some properties defined as the function (see here for details). For example:
postData: {
SessionID: function() {
return $('#eSessionID3').val();
},
rowDataAsObjects: false,
queryRowCount: true,
dataType: 'data',
recordOffset: function() {
var pd = jQuery("#list2")[0].p.postData;
return pd.recordLimit * (pd.page - 1);
},
json: function() {
var pd = jQuery("#list2")[0].p.postData;
return {
SessionID: $('#eSessionID3').val(),
dataType: 'data',
recordOffset: pd.recordLimit * (pd.page - 1),
rowDataAsObjects: false,
queryRowCount: true,
sort_fields: pd.sort_fields
};
}
}
I used here both json parameter which you currently use and add parameters like SessionID, queryRowCount and so on directly in the list of parameters which will be send. Of course it is enough to send only one way (either json or the rest) to send the aditional information which you need.
The second example is incorrect, as you are declaring a javascript object as the body of a function, what you could do is:
function serialLoad() {
// Return an object with the required members
return {
serializeGridData: function(postData) { ... },
loadError: function(xhr, msg, e) { ... }
};
}
You are mixing function declaration and object literal notation. This syntax: property: value is used when creating an object with object literal notation:
var obj = {
prop: val,
prop2: val
};
serializeGridData and loadError are properties of some object and you cannot define those by just putting them into a function.
One way would be to create two functions, one for serializeGridData and one for loadError, e.g.
function serialLoad(postData){
var jsonParams = {
//...
};
if (postData.sord == 'desc') {
//... some code here
}
else {
//... some code here
}
return 'json=' + jsonParams;
}
function onError(xhr, msg, e) {
showMessage('errmsg');
}
Then you can assign them in your other file to the object:
// ... some code here
serializeGridData: serialLoad,
loadError: onError,
//... some code here
Another way is to pass the object in question to the function and assign the properties there:
function attachLoadHandler(obj) {
obj.serializeGridData = function(postData) {
//...
};
obj.loadError = function(xhr, msg, e) {
//...
};
}
Then you have to pass the object you created to that function:
attachLoadHandler(obj);
But I think the first approach is easier to understand.

Categories

Resources