Passing an error message from controller to view with Backbone - javascript

I have the following error message available as HTML:
<div class="alert alert-error">
<div class="content">
<p>
<i class="fa fa-warning"></i>
User not found.
</p>
</div>
</div>
How can I display it for the user without losing good practices, in Backbone?
I mean, what's the best way to make my view render it with reliability?
When is this message displayed?
user.save({}, {
success: function(model, response) {
if (response.error) {
// hey, the message goes here!
}
}
});

You can extend Backbone View with a new method to display errors like this :
_.extend(Backbone.View.prototype, {
showError: function(message) {
// Here you render your error, may be like this : $('#errorInfo').html(message);
}
});
and in your view :
var self = this; // I assume that you are in the view
user.save({}, {
success: function(model, response) {
if (response.error) {
self.showError(/* things you want to pass */);
}
}
});

For me the best practice is to create an object errorMessageView
HTML :
<div id="errorInfo"></div>
JS :
var errorMessageView = Backbone.View.extend({
el: "#errorInfo",
initialize: function() {
},
renderInnerHTML: function(message) {
this.el.innerHTML = message;
}
});
var errorMessage = new errorMessageView ;
user.save({}, {
success: function(model, response) {
if (response.error) {
errorMessage.renderInnerHTML(response.error);
}
}
});

Related

ng-show not working properly - AngularJS

I'm using AngularJS 1.6.6 to develop a web app. I'm using ng-show on my template to recycle it:
<div >
<br/>
<div class="form">
<form data-ng-submit="objectHandlerVM.functions.objectHandlerSwitcher()">
<button data-ng-show="objectHandlerVM.functions.where('/editObject/'+objectHandlerVM.object.idn)">Edit</button>
<button data-ng-show="objectHandlerVM.functions.where('/createObject')">Create</button>
<button data-ng-show="objectHandlerVM.functions.where('/deleteObject/'+objectHandlerVM.object.idn)">Delete</button>
</form>
</div>
</div>
This is trying to show a different button depending on the url used to access.
Here is the code of the controller:
angular.module('objectApp')
.controller('objectHandlerCtrl', ['objectFactory','usersFactory','$routeParams','$location',
function(objectFactory,usersFactory,$routeParams,$location){
var objectHandlerViewModel = this;
objectHandlerViewModel.object={};
objectHandlerViewModel.functions = {
where : function(route){
return $location.path() == route;
},
readUserNameEmail : function() {
usersFactory.getUser()
.then(function(response){
objectHandlerViewModel.object.title= response.title;
objectHandlerViewModel.object.content= response.content;
console.log("Reading user with id: ",response.idn," Response: ", response);
}, function(response){
console.log("Error reading user data");
})
},
updateObject : function() {
objectFactory.putObject(objectHandlerViewModel.object)
.then(function(response){
console.log("Updating object with id:",objectHandlerViewModel.object.idn," Response:", response);
}, function(response){
console.log("Error updating object");
})
},
createObject : function() {
objectFactory.postObject(objectHandlerViewModel.object)
.then(function(response){
console.log("Creating object. Response:", response);
}, function(response){
console.log("Error creating the object");
})
},
deleteObject : function(id) {
objectFactory.deleteObject(id)
.then(function(response){
console.log("Deleting object with id:",id," Response:", response);
}, function(response){
console.log("Error deleting object");
})
},
objectHandlerSwitcher : function(){
if (objectHandlerViewModel.functions.where('/createObject')){
console.log($location.path());
objectHandlerViewModel.functions.createObject();
}
else if (objectHandlerViewModel.functions.where('/editObject/'+objectHandlerViewModel.object.idn)){
console.log($location.path());
objectHandlerViewModel.functions.updateObject();
}
else if (objectHandlerViewModel.functions.where('/deleteObject/'+objectHandlerViewModel.object.idn)){
console.log($location.path());
objectHandlerViewModel.functions.deleteObject(objectHandlerViewModel.object.idn);
}
else {
console.log($location.path());
}
$location.path('/');
}
}
console.log("Entering objectHandlerCtrl with $routeParams.ID=",$routeParams.ID);
if ($routeParams.ID==undefined) objectHandlerViewModel.functions.readUserNameEmail();
else objectHandlerViewModel.functions.readObject($routeParams.ID);
}]);
So, when I access that template to create an object, the url ends with "createObject" and it should show only one of the buttons, and the same with "editObject" and "deleteObject". But, don't know why, is showing the three of them.
I've also tried:
<button data-ng-show="objectHandlerVM.object.idn!=undefined">Edit</button>
<button data-ng-show="objectHandlerVM.object.idn==undefined">Create</button>
And it also shows both of them, which is confusing me the most...
ng-show works with boolean, if you want to check the url, you can have a function to do that and return true/false based on that,
<button data-ng-show="checkUrl()">Edit</button>
and in controller
$scope.checkUrl = function(){
//add your logic
return true/false;
}

Get the article's view times using Vue.js and Laravel 5.3

My thought process:
When the show page opens, get the article's id with JavaScript.
Check this id exist or not in cookie
If not exists, write it into cookie and send an ajax request, the backend updates view times.
If exists, do nothing.
Demo:
View:
<div class="card">
<div class="card-block text-xs-center">
<h5 class="card-title">{{$article->title}}</h5>
<hr class="m-y-2">
<h6 class="card-subtitle text-muted">date:{{$article->created_at->format('Y-m-d')}}
    views:{{$article->view_times}}</h6>
</div>
<div class="card-block">
<p class="card-text">{{$article->content}}</p>
</div>
</div>
Controller:
class ArticlesController extends Controller
{
//`show` method
public function show($id)
{
$article = Article::findOrFail($id);
return view('show', compact('article'));
}
//the method of updating view times.
public function statistics(Request $request)
{
$id = $request->input('id');
$article = Article::findOrFail($id);
$view_time=$article->view_time;
$article->view_time=$view_time+1;
$article->save();
}
}
JavaScript:
Vue.http.headers.common['X-CSRF-TOKEN'] = document.querySelector('meta[name=csrf-token]').getAttribute('content')
Vue.http.options.emulateJSON = true;
var vm = new Vue({
el: "body",
data: function(){
return{
id:[]
}
},
created() {
//1、Get the article's id.Do I have to send an ajax request? Is there any other way?
this.$http.get('article/get-id').then((response) => {
// success callback
this.id=response.data;
}, (response) => {
// error callback
});
//2、After Getting the `id`,check it in cookie,I don't know how to do it?
//3、If not exists,write it into cookie and send an ajax request,how to write the if() sentence?
if(){
var formData = new FormData();
var id=this.id;
formData.append('id',id);
this.$http.patch('article/statistics', formData,{
before(request) {
if (this.previousRequest) {
this.previousRequest.abort();
}
this.previousRequest = request;
}
}).then((response) => {
// success callback
}, (response) => {
// error callback
});
}
}
});
Questions:
There are three questions, shown as comments in JavaScript code above.

Deal with the result of postJSON

I'm trying to implement something with jQuery and Vue.js:
And here is my script part:
<script>
function initVM(result) {
// alert(result.num)
var vm2 = new Vue({
el: '#vm2',
data: {
// ③bind one item of the result as example
rrr: result.num
}
});
$('#vm2').show();
}
$(function() {
var vm = new Vue({
el: '#vm',
data: {
content: ''
},
methods: {
submit: function(event) {
event.preventDefault();
var
$form = $('#vm'),
content = this.content.trim();
// ①post textarea content to backend
$form.postJSON('/api/parse', {
content: content
}, function(err, result) {
if (err) {
$form.showFormError(err);
}
else {
// ②receive a result dictionary
$('#vm').hide();
initVM(result);
}
});
}
}
});
});
</script>
Here is my html part:
<html>
<form id="vm", v-on="submit: submit">
<textarea v-model="content" name="content"></textarea>
<button type="submit">Have a try!</button>
</form>
<div id="vm2" style="diplay:none;">
<!-- ④show the result-->
The result:
{{ rrr }}
</div>
</html>
Here is the definition of postJSON
<script>
// ...
postJSON: function (url, data, callback) {
if (arguments.length===2) {
callback = data;
data = {};
}
return this.each(function () {
var $form = $(this);
$form.showFormError();
$form.showFormLoading(true);
_httpJSON('POST', url, data, function (err, r) {
if (err) {
$form.showFormError(err);
$form.showFormLoading(false);
}
callback && callback(err, r);
});
});
// ...
// _httpJSON
function _httpJSON(method, url, data, callback) {
var opt = {
type: method,
dataType: 'json'
};
if (method==='GET') {
opt.url = url + '?' + data;
}
if (method==='POST') {
opt.url = url;
opt.data = JSON.stringify(data || {});
opt.contentType = 'application/json';
}
$.ajax(opt).done(function (r) {
if (r && r.error) {
return callback(r);
}
return callback(null, r);
}).fail(function (jqXHR, textStatus) {
return callback({'error': 'http_bad_response', 'data': '' + jqXHR.status, 'message': 'something wrong! (HTTP ' + jqXHR.status + ')'});
});
}
</script>
The process can be described as:
Post the content to backend
Receive the result and hide the form
Create a new Vue with the result
Show the result in a div which is binding with the new Vue instance
Actually, I do receive the result successfully, which is proved by the alert(result.num) statement, but nothing find in vm2's div except The result:
Where is the problem? Or please be free to teach me a simpler approach if there is, because I don't think what I am using is a good one.
Here's questioner.
Finally I found where is the problem.
The problem lays in Mustache: {{ }}
I use jinja2, a template engine for Python and Vue.js, a MVVM frontend framework. Both of them use {{ }} as delimiters.
So if anyone got trapped in the same situation with me, which I don't think there will be, please:
app.jinja_env.variable_start_string = '{{ '
app.jinja_env.variable_end_string = ' }}' # change jinjia2 config
OR
Vue.config.delimiters = ['${', '}'] # change vue config

Updating ng-show from within .then()

I have a loader that I show while an async service call is completed, and simply want to hide the loader when complete. Here is my controller:
app.controller('DataController',
function($scope, DataService) {
// UI state
$scope.loading = true;
DataService.getData({ "count": 10 }).then(function(data) {
$scope.data = data;
// UI state
$scope.loading = false; // does not update ng-view
$scope.$apply(function() { // generates error
$scope.loading = false;
});
});
});
And the view:
<div ng-controller="DataController">
<div id="container">
<div>
{{ loading }}
</div>
<div class="spinner large" ng-show="loading"></div>
<div class="data-container" ng-show="!loading">
...
</div>
</div>
</div>
Note the the {{ loading }} value gets updated properly in the view. Using the wrapping $scope.$apply() call resulted in an error:
Error: [$rootScope:inprog]
UPDATE
As this might be promise-related, here's the promise generating getData() method from the DataService factory:
getData: function(params) {
var deferred = $q.defer();
APIService.data(params).then(function(data) {
deferred.resolve(data);
});
return deferred.promise;
}
And the last piece, the APIService.data() method:
data: function(params) {
var deferred = $q.defer();
$resource(endpoint + '/data/feed', {}, {
'query': {
method: 'POST',
headers: headers
}
}).query(params).$promise.then(function(data) {
deferred.resolve(data);
});
return deferred.promise;
}
I would solve this by binding the show/hide directive to the data-property in the controller. It will be the same as false if the data is undefined.
<div class="spinner large" ng-hide="data"></div>
<div class="data-container" ng-show="data">
Try to use
$scope.$evalAsync(function() {
$scope.loading = false;
});
Found the issue - as this is in a Chrome Extension, I needed to include the Angular CSS CSP file, which includes the ng-hide class definition. Including that file resulted in the code working as expected. Thanks everyone for the help. More info:
https://docs.angularjs.org/api/ng/directive/ngCsp

angularjs variable not showing when called before $http

In my angular app, i have a message service to display info, loading and error messages for my app. It looks like that:
module.factory('msgSvc', function(){
var current_message = '';
return {
message: function(){
return current_message;
},
setMessage: function(msg){
console.log('setting message: '+ msg);
current_message = msg;
},
clear: function(){ current_message = null; current_style = null}
}
});
and in my view i have
<span class="pull-left label label-warning" >{{ msg.message() }}</span>
I have a login controller, when the user submits the form i want to show a "logging you in..." message while an ajax login is sent. and an error message if there was an error. here's my code:
function LoginCtrl($scope, $http, msgSvc) {
[...]
$scope.login = function(creds) {
console.log(creds);
msgSvc.setMessage('Logging in...');
$http.post('http://...',creds)
.success(function(data){
console.log(data);
[...]
msgSvc.clear();
$location.path('/');
})
.error(function(data, status){
console.log(status);
msgSvc.setMessage('Wrong username or password.');
});
};
}
login() is called by the submit form, Logging in... never shows even though the function is called (it appears in the console). but the error message appears.
am i doing something wrong?
edit: the login form
<form class="form">
<input type="text" placeholder="Username" ng-model="loginCreds.username" required />
<input type="password" placeholder="Password" ng-model="loginCreds.password" required />
<button ng-click="login(loginCreds)">Login</button>
</form>
edit 2
If it changes anything, there are many controllers setting messages in the service and in the actual code, the controller showing the message (where the $scope.msg variable is set) is different from the one setting the message.
function BodyCtrl($scope, msgSvc) {
$scope.msg = msgSvc;
}
There are couple problems with your implementation:
As the message is being set in a private variable, you would need use $watch for the message to be displayed;
A .factory is a singleton and therefore setMessage would have set the same message for all controllers.
The simplest solution is to pass the controller's $scope to the svcMsg:
app.factory("msgSvc", function () {
return function (scope) {
var priv_scope = scope;
this.setMessage = function (msg) {
console.log('setting message: '+ msg);
priv_scope.message = msg;
};
this.clear = function () {
priv_scope.message = "";
};
};
});
In you controller, you would then do:
var msg = new msgSvc($scope);
In case you do want to propagate the message to all controllers, use $rootScope:
app.service("msgSvc", function ($rootScope) {
var priv_scope = $rootScope;
this.setMessage = function (msg) {
console.log('setting message: '+ msg);
priv_scope.message = msg;
};
this.clear = function () {
priv_scope.message = "";
};
});
Check out this Plunker using $rootScope:
http://plnkr.co/edit/NYEABNvjrk8diNTwc3pP?p=preview
As $rootScope is really a global variable in Angular, you shouldn't abuse it. It can also be problematic if you accidentally set the $scope.message in controllers. An alternative is to use $watch to detect the change to the message:
// In your controller, do:
$scope.$watch(
function () {
return msgSvc.message;
},
function () {
$scope.message = msgSvc.message;
}
)
Here is an example using $watch:
http://plnkr.co/edit/vDV2mf?p=info
Set $scope.msg in each place you want to display the value on the view:
function LoginCtrl($scope, $http, msgSvc) {
[...]
$scope.msg = "";
$scope.login = function(creds) {
console.log(creds);
$scope.msg = msgSvc.setMessage('Logging in...');
$http.post('http://...',creds)
.success(function(data){
console.log(data);
[...]
$scope.msg = msgSvc.clear();
$location.path('/');
})
.error(function(data, status){
console.log(status);
$scope.msg = msgSvc.setMessage('Wrong username or password.');
});
};
}
I know you may be trying to avoid that but the changes in its value are not being propagated.
Add $apply to in error()
function LoginCtrl($scope, $http, msgSvc) {
[...]
$scope.login = function(creds) {
console.log(creds);
msgSvc.setMessage('Logging in...');
$scope.msg = msgSvc;
$http.post('http://...',creds)
.success(function(data){
console.log(data);
[...]
msgSvc.clear();
$location.path('/');
})
.error(function(data, status){
$scope.$apply(function() {
msgSvc.setMessage('Wrong username or password.');
});
});
};
}
SEE DEMO

Categories